SwipeLayout 可以左滑的一个控件

先看一下效果图



1、左右两个控件都可以自己设置点击事件

2、右侧控件有个动画,300ms

3、右侧的临界点是 右侧控件宽度/2

4、最大的亮点是没有任何侵入,大家可以直接使用源码。看布局代码就能了解

注意点:只能用两个直接子View,并且第一个子View 是具体内容,第二个子View是隐藏控件


reset ()函数 用于复用时候的重置状态

import android.content.Context;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.Scroller;

import com.chinabim.smartconstructionsite.utils.Logger;

/**
 * @author 高延荣
 * @date 2018/4/24 12:14
 * 描述:  项目列表Item,附带有左划功能,显示 不再提醒 按钮
 */

public class SwipeLayout extends LinearLayout {
    private Context mContext;
    /**
     * 内容部分
     */
    private View viewTop;
    /**
     * 删除按钮
     */
    private View viewBottom;
    /**
     * top 宽度
     */
    private int viewTopWidth;
    /**
     * bottom 宽度
     */
    private int viewBottomWidth;
    /**
     * 滑动计算器
     */
    private Scroller scroller;


    public SwipeLayout(Context context) {
        super(context);
        initView(context);
    }

    public SwipeLayout(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initView(context);
    }

    private void initView(Context context) {
        mContext = context;
        scroller = new Scroller(context);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        if (getChildCount() != 2) {
            throw new IllegalStateException(SwipeLayout.class.getSimpleName() + "必须有且只有两个子控件");
        }
        viewTop = getChildAt(0);
        viewBottom = getChildAt(1);
    }


    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        viewTop.measure(widthMeasureSpec, heightMeasureSpec);
    }

    private boolean loadOne;


    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if (!loadOne) {
            loadOne = true;
            viewTopWidth = viewTop.getWidth();
            viewBottomWidth = viewBottom.getWidth();
            //  @see reset()
            if (viewBottomVisible) {
                scrollBy(viewBottomWidth, 0);
            }
        }
    }

    private float xDown;
    private float xOldDown;
    private float yDown;
    private float xMove;
    private float yMove;
    private float xDistance;
    private float xAbsDistance;
    private float yDistance;

    private boolean viewBottomVisible;

    /**
     * 临界值设为 50px , 保证不会稍微动一点点就会拦截事件
     */
    private static final float criticalValue = 50;

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (!scroller.isFinished()) {
                    scroller.forceFinished(true);
                }

                curState = TOUCH_DOWN;

                xOldDown = xDown = ev.getX();
                yDown = ev.getY();

                break;
            case MotionEvent.ACTION_MOVE:

                xMove = ev.getX();
                yMove = ev.getY();

                xDistance = xMove - xDown;
                yDistance = yMove - yDown;
                // 先判断用户是左右滑动还是上下滑动,因为用户可能是先右滑,再左滑,所以使用绝对值,但是右滑不需要进行滚动,所以在 onTouch方法中进行判断
                if (Math.abs(xDistance) > criticalValue && Math.abs(xDistance) > Math.abs(yDistance)) {
                    return true;
                }

                break;
            default:
        }
        return super.onInterceptTouchEvent(ev);
    }


    private static final byte TOUCH_DOWN = 0;
    private static final byte TOUCH_MOVE = 1;
    private static final byte TOUCH_UP = 2;
    private byte curState = TOUCH_DOWN;

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_MOVE:
                xMove = event.getX();
                yMove = event.getY();

                xDistance = xMove - xDown;
                yDistance = yMove - yDown;

                xDown = xMove;

                xAbsDistance = xMove - xOldDown;

                if (curState == TOUCH_DOWN) {
                    if (Math.abs(xDistance) > Math.abs(yDistance)) {
                        curState = TOUCH_MOVE;
                    }
                }

                // 当移动时,判断viewBottom状态,如果显示,则只能右拉,如果不显示则只能左拉
                if (curState == TOUCH_MOVE) {
                    if (viewBottomVisible) {
                        if (xAbsDistance >= 0 && Math.abs(xAbsDistance) <= viewBottomWidth) {
                            scrollBy(-(int) xDistance, 0);
                        }
                    } else {
                        if (xAbsDistance <= 0 && Math.abs(xAbsDistance) <= viewBottomWidth) {
                            scrollBy(-(int) xDistance, 0);
                        }
                    }
                }
                return true;

            // 抬起时,直接scrollBy 很生硬,使用 scroller
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                if (curState == TOUCH_MOVE) {
                    curState = TOUCH_UP;
                    int moveWidth;
                    if (getScrollX() >= viewBottomWidth / 2) {
                        viewBottomVisible = true;
                        moveWidth = viewBottomWidth - getScrollX();
                    } else {
                        viewBottomVisible = false;
                        moveWidth = -getScrollX();
                    }

                    if (onBottomViewChangedListener != null) {
                        onBottomViewChangedListener.onBottomViewChanged(viewBottomVisible);
                    }
                    if (moveWidth != 0) {
                        scroller.startScroll(getScrollX(), 0, moveWidth, 0, 300);
                        postInvalidate();
                    }

                    return true;
                }
                break;
            default:
        }
        return super.onTouchEvent(event);
    }

    @Override
    public void computeScroll() {
        super.computeScroll();
        if (scroller.computeScrollOffset() && curState == TOUCH_UP) {
            int v = scroller.getCurrX();
            //执行 scrollTo
            if (v < 0) {
                v = 0;
            }
            scrollTo(v, 0);
            postInvalidate();
        }
    }


    //////////////////////////////////////////////////////////////////////////////

    /**
     * 在RecyclerView中,使用SparseArrayCompat<Boolean> 记录当前 position 对应的 显示状态
     */
    public interface OnBottomViewChangedListener {
        void onBottomViewChanged(boolean viewBottomVisible);
    }

    public OnBottomViewChangedListener getOnBottomViewChangedListener() {
        return onBottomViewChangedListener;
    }

    private OnBottomViewChangedListener onBottomViewChangedListener;

    public void setOnBottomViewChangedListener(OnBottomViewChangedListener onBottomViewChangedListener) {
        this.onBottomViewChangedListener = onBottomViewChangedListener;
    }


    /**
     * 重置考虑两种情况
     * 1、控件已存在,那这时候 viewBottomWidth != 0,直接 reset就可以
     * 2、控件不存在,那这时候的控件只是在View树存在对象,即只是完成了 onFinishInflate() 函数,但是还未走测量等方法,这时候就需要在
     * onLayout内对控件进行布局设置。
     */
    public void reset(boolean viewBottomVisible) {
        this.viewBottomVisible = viewBottomVisible;
        if (viewBottomWidth == 0) {
            return;
        }
        if (viewBottomVisible) {
            scrollBy(getScrollX() == 0 ? viewBottomWidth : 0, 0);
        } else {
            scrollBy(getScrollX() != 0 ? -getScrollX() : 0, 0);
        }
    }
}

Demo

<?xml version="1.0" encoding="utf-8"?><!-- 项目列表中,group -->
<你的路径.SwipeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:layout_marginTop="@dimen/dp_4"
    android:orientation="horizontal">

    <RelativeLayout
        android:clickable="true"
        android:id="@+id/topView"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1">

        <TextView
            android:id="@+id/itemTitle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_marginLeft="@dimen/dp_20"
            android:maxLines="1"
            android:text="项目名称"
            android:textColor="@color/_000" />

        <TextView
            android:id="@+id/itemNumber"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:layout_centerVertical="true"
            android:layout_marginRight="@dimen/dp_20"
            android:src="@drawable/arrows_bottom"
            android:text="编号:123456"
            android:textColor="@color/_000" />

    </RelativeLayout>

    <TextView
        android:clickable="true"
        android:id="@+id/bottomView"
        android:layout_width="100dp"
        android:layout_height="match_parent"
        android:background="@color/_DE1E25"
        android:gravity="center"
        android:text="不再显示"
        android:textColor="@color/_FFF" />

</com.chinabim.smartconstructionsite.ui.extend.SwipeLayout>


这样就可以了,大家在 getView里面找到控件,然后设置监听。


//////////////////////////////////////////////////////////////////

附带 AC  和 Adapter代码

AC

import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.widget.FrameLayout;
import android.widget.TextView;

import com.chad.library.adapter.base.entity.MultiItemEntity;
import com.chinabim.smartconstructionsite.R;
import com.chinabim.smartconstructionsite.adapter.ProjectListAdapter;
import com.chinabim.smartconstructionsite.entity.ProjectListGroup;
import com.chinabim.smartconstructionsite.entity.ProjectListItem;
import com.chinabim.smartconstructionsite.feature.BaseActivity;

import java.util.ArrayList;
import java.util.Random;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;

import static com.chad.library.adapter.base.BaseQuickAdapter.SLIDEIN_RIGHT;

/**
 * @author 高延荣
 * @date 2018/4/23 16:42
 * 描述:  项目列表
 */

public class ProjectListActivity extends BaseActivity {
    @BindView(R.id.back)
    FrameLayout back;
    @BindView(R.id.title)
    TextView title;
    @BindView(R.id.recyclerView)
    RecyclerView recyclerView;

    /**
     * 数据集合
     */
    private ArrayList<MultiItemEntity> list;
    /**
     * 适配器
     */
    private ProjectListAdapter adapter;

    @Override
    protected int initLayoutID() {
        return R.layout.activity_project_list;
    }

    @Override
    protected void initContentView() {
        title.setText("项目列表");
        list = generateData();
        adapter = new ProjectListAdapter(list);
        LinearLayoutManager manager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(manager);
        recyclerView.setAdapter(adapter);
        adapter.openLoadAnimation(SLIDEIN_RIGHT);
//        adapter.expandAll();
    }

    private ArrayList<MultiItemEntity> generateData() {

        Random random = new Random();

        ArrayList<MultiItemEntity> res = new ArrayList<>();

        String[] groups = {"我创建的项目", "我加入的项目"};
        String[] items = {"长寿村旧村改造二期工程", "二通厂场地建设", "马家堡东路金胜嘉谊家园", "中国电信大厦项目工程组", "中白产业园项目",
                "长寿村旧村改造二期工程", "二通厂场地建设", "马家堡东路金胜嘉谊家园", "中国电信大厦项目工程组", "中白产业园项目",
                "长寿村旧村改造二期工程", "二通厂场地建设", "马家堡东路金胜嘉谊家园", "中国电信大厦项目工程组", "中白产业园项目"};
        String[] nums = {"25003", "30147", "56244", "95270", "46380"};
        int groupCount = groups.length;
        int itemCount = items.length;

        for (int i = 0; i < groupCount; i++) {
            ProjectListGroup group = new ProjectListGroup(groups[i]);
            for (int k = 0; k < itemCount; k++) {
                ProjectListItem item = new ProjectListItem(items[random.nextInt(5)], nums[random.nextInt(5)],i * itemCount + k);
                group.addSubItem(item);
            }
            res.add(group);
        }


        return res;
    }

    @OnClick(R.id.back)
    public void onViewClicked() {
        finish();
    }
}

Adapter

import android.support.v4.util.SparseArrayCompat;
import android.view.View;
import android.widget.TextView;

import com.chad.library.adapter.base.BaseMultiItemQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import com.chad.library.adapter.base.entity.MultiItemEntity;
import com.chinabim.smartconstructionsite.R;
import com.chinabim.smartconstructionsite.entity.ProjectListGroup;
import com.chinabim.smartconstructionsite.entity.ProjectListItem;
import com.chinabim.smartconstructionsite.ui.extend.SwipeLayout;
import com.chinabim.smartconstructionsite.utils.Tool;

import java.util.List;

/**
 * @author 高延荣
 * @date 2018/4/23 18:32
 * 描述:
 */

public class ProjectListAdapter extends BaseMultiItemQuickAdapter<MultiItemEntity, BaseViewHolder> {


    /**
     * 当前条目类型,这里0表示group
     */
    public static final int TYPE_LEVEL_0 = 0;

    /**
     * 当前条目类型,这里1表示group下的Item
     */
    public static final int TYPE_LEVEL_1 = 1;


    /**
     * Same as QuickAdapter#QuickAdapter(Context,int) but with
     * some initialization data.
     *
     * @param data A new list is created out of this one to avoid mutable list
     */
    public ProjectListAdapter(List<MultiItemEntity> data) {
        super(data);
        //  设置条目类型 及 对应的布局文件样式
        addItemType(TYPE_LEVEL_0, R.layout.project_list_group);
        addItemType(TYPE_LEVEL_1, R.layout.project_list_item);
    }

    @Override
    protected void convert(final BaseViewHolder helper, final MultiItemEntity item) {
        switch (helper.getItemViewType()) {
            case TYPE_LEVEL_0:
                final ProjectListGroup group = (ProjectListGroup) item;
                helper.setText(R.id.groupTitle, group.title)
                        .setImageResource(R.id.groupArrows, group.isExpanded() ? R.drawable.arrows_bottom : R.drawable.arrows_right);

                final int pos = helper.getAdapterPosition();
                View viewLine = helper.getView(R.id.viewLine);
                viewLine.setVisibility(pos != 0 ? View.VISIBLE : View.GONE);

                helper.itemView.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        if (group.isExpanded()) {
                            collapse(pos);
                        } else {
                            expand(pos);
                        }
                    }
                });
                break;
            case TYPE_LEVEL_1:
                final ProjectListItem item1 = (ProjectListItem) item;
                helper.setText(R.id.itemTitle, item1.name)
                        .setText(R.id.itemNumber, item1.number);

                final int position = item1.position;

                SwipeLayout swipeLayout = (SwipeLayout) helper.itemView;
                swipeLayout.reset(sparseArrayCompat.get(position, false));


                swipeLayout.setOnBottomViewChangedListener(new SwipeLayout.OnBottomViewChangedListener() {
                    @Override
                    public void onBottomViewChanged(boolean viewBottomVisible) {
                        sparseArrayCompat.append(position, viewBottomVisible);
                    }
                });


                helper.getView(R.id.topView).setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Tool.showToastCenter(item1.name);
                    }
                });

                helper.getView(R.id.bottomView).setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Tool.showToastCenter("不再提示");
                    }
                });

                break;
            default:
        }
    }

    private SparseArrayCompat<Boolean> sparseArrayCompat = new SparseArrayCompat<>();

}

ProjectListGroup

import com.chad.library.adapter.base.entity.AbstractExpandableItem;
import com.chad.library.adapter.base.entity.MultiItemEntity;
import com.chinabim.smartconstructionsite.adapter.ProjectListAdapter;

/**
 * @author 高延荣
 * @date 2018/4/24 10:35
 * 描述: ProjectListActivity 项目列表中,项目组,既  自由项目,加入项目
 */

public class ProjectListGroup extends AbstractExpandableItem<ProjectListItem> implements MultiItemEntity {

    public ProjectListGroup(String title) {
        this.title = title;
    }

    public String title;


    @Override
    public int getLevel() {
        return 0;
    }

    @Override
    public int getItemType() {
        return ProjectListAdapter.TYPE_LEVEL_0;
    }
}

ProjectListItem

import com.chad.library.adapter.base.entity.MultiItemEntity;
import com.chinabim.smartconstructionsite.adapter.ProjectListAdapter;

/**
 * @author 高延荣
 * @date 2018/4/24 10:36
 * 描述:  ProjectListActivity 项目列表中,项目item
 */

public class ProjectListItem implements MultiItemEntity {

    public String name;
    public String number;
    public int position;

    public ProjectListItem(String name, String number, int position) {
        this.name = name;
        this.number = number;
        this.position = position;
    }

    @Override
    public int getItemType() {
        return ProjectListAdapter.TYPE_LEVEL_1;
    }
}

猜你喜欢

转载自blog.csdn.net/qq_26030147/article/details/80068585