Android——实现RecyclerView左侧滑删除与右侧滑选择

项目中要实现的功能,之前找了很久发现网上大部分的侧滑删除和列表全选都是ListView的实现,而对RecyclerView的实现却是少之又少,所以花了很多时间实现了一个还比较满意的版本,

效果如下:

侧滑删除(带自动校位滑动效果):


右滑出现选择框:


一键编辑(全选):



实现原理:

1.首先需要实现一个基本的RecyclerView。

2. 自定义Item的布局。

3.结合自定义item布局通过自定义Item项实现左右滑效果。


代码实现:

1.首先是实现RecyclerView的基本用法,这里我们先要实现item的布局:layout_item.xml

<?xml version="1.0" encoding="utf-8"?>
<com.ng.ngrecyclerview.view.SlidingButtonView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="60dp"
    android:layout_marginBottom="1dp"
    android:background="@android:color/white">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <TextView
            android:id="@+id/tv_delete"
            android:layout_width="80dp"
            android:layout_height="match_parent"
            android:layout_toRightOf="@+id/layout_content"
            android:background="@drawable/btn_click_red_havebackground"
            android:gravity="center"
            android:text="删 除"
            android:textColor="#DDFFFFFF" />

        <LinearLayout
            android:id="@+id/layout_content"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:background="#0280ff"
            android:orientation="horizontal">

            <RelativeLayout
                android:id="@+id/rl_left"
                android:layout_width="50dp"
                android:layout_height="match_parent">

                <RadioButton
                    android:id="@+id/rbtn"
                    android:layout_width="50dp"
                    android:layout_height="match_parent"
                    android:layout_centerHorizontal="true"
                    android:layout_centerVertical="true" />
            </RelativeLayout>

            <TextView
                android:id="@+id/text"
                android:layout_width="wrap_content"
                android:layout_height="match_parent"
                android:background="@drawable/btn_click_black_havebackground"
                android:gravity="center"
                android:textColor="#DD000000"
                android:textSize="50dp" />
        </LinearLayout>

    </RelativeLayout>

</com.ng.ngrecyclerview.view.SlidingButtonView>
布局预览:

注意:因为逻辑问题所以可以这样划分:id为rl_left的RelativeLayout为左侧想自定义的布局,可以直接在这个RelativeLayout里修改,id为text的TextView为中部部分布局,可以替换为其他Viewgroup并自定义。

删除布局是id为tv_delete的TextView,也可以随意自定义替换。

2.Adapter实现:

public class MyAdapter extends RecyclerView.Adapter implements SlidingButtonView.IonSlidingButtonListener {

    Context context;
    private IonSlidingViewClickListener mIDeleteBtnClickListener;

    private List<String> mDatas = new ArrayList<String>();

    private SlidingButtonView mMenu = null;

    public MyAdapter(Context context, ArrayList<String> date) {
        this.context = context;
        this.mDatas = date;
        mIDeleteBtnClickListener = (IonSlidingViewClickListener) context;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(context).inflate(R.layout.layout_item, parent, false);
        return new MyViewHolder(view);
    }

    boolean allopen = false;

    public void setAllopen(boolean allopen) {
        this.allopen = allopen;

    }

    @Override
    public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
        final MyViewHolder viewHolder = (MyViewHolder) holder;
        viewHolder.slidingButtonView.setSlidingButtonListener(MyAdapter.this);

        viewHolder.textView.setText(mDatas.get(position));

        //设置内容布局的宽为屏幕宽度
        viewHolder.layout_content.getLayoutParams().width = Utils.getScreenWidth(context) + viewHolder.rl_left.getLayoutParams().width;
//        viewHolder.textView.setOnClickListener(new View.OnClickListener() {
//            @Override
//            public void onClick(View v) {
//                //判断是否有删除菜单打开
                if (menuIsOpen()) {
                    closeMenu();//关闭菜单
                } else {
                    int n = viewHolder.getLayoutPosition();
                    mIDeleteBtnClickListener.onItemClick(v, n);
                }
//
//            }
//        });

        viewHolder.btn_Delete.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int n = holder.getLayoutPosition();
                mIDeleteBtnClickListener.onDeleteBtnCilck(v, n);
            }
        });
        LogUtils.d("项:" + position + "是否开:" + allopen);
        if (allopen) {
            LogUtils.d("打开?");
            viewHolder.slidingButtonView.openMenu();
            viewHolder.slidingButtonView.setCanTouch(false);
        } else {
            viewHolder.slidingButtonView.closeMenu();
            viewHolder.slidingButtonView.setCanTouch(true);
        }

    }

    @Override
    public int getItemCount() {
        return mDatas.size();
    }

    /**
     * 删除菜单打开信息接收
     */
    @Override
    public void onMenuIsOpen(View view) {
        mMenu = (SlidingButtonView) view;
    }

    /**
     * 滑动或者点击了Item监听
     *
     * @param slidingButtonView
     */
    @Override
    public void onDownOrMove(SlidingButtonView slidingButtonView) {
        if (menuIsOpen()) {
            if (mMenu != slidingButtonView) {
                closeMenu();
            }
        }
    }

    /**
     * 关闭菜单
     */
    public void closeMenu() {
        mMenu.closeMenu();
        mMenu = null;

    }

    /**
     * 判断是否有菜单打开
     */
    public Boolean menuIsOpen() {
        if (mMenu != null) {
            return true;
        }
        return false;
    }

    public interface IonSlidingViewClickListener {
        void onItemClick(View view, int position);

        void onDeleteBtnCilck(View view, int position);
    }

    public void addData(int position) {
        mDatas.add(position, "添加项");
        notifyItemInserted(position);
    }

    public void removeData(int position) {
        mDatas.remove(position);
        notifyItemRemoved(position);

    }

}
这里没有太多要说的,主要就是一个判断是否打开左侧编辑菜单的标识符boolean值allopen的判定方法,其他都为基础的RecyclerView的Adapter使用方法。

在Activity中的使用也是基础的方法,这里不提,后面源码下载可以看到。

3.Item侧滑效果实现类SlidingButtonView:

public class SlidingButtonView extends HorizontalScrollView {

    //删除按钮
    private TextView mTextView_Delete;

    //左侧控件
    private RadioButton rbtn;

    private TextView text;

    private int leftWidth;

    //记录滚动条滚动的距离
    private int mScrollWidth;

    public int getLeftWidth() {
        return leftWidth;
    }


    //自定义的接口,用于传达滑动事件
    private IonSlidingButtonListener mIonSlidingButtonListener;

    //记录按钮菜单是否打开,默认关闭false
    private Boolean isOpen = false;

    public Boolean getOpen() {
        return isOpen;
    }

    public void setOpen(Boolean open) {
        isOpen = open;
    }

    //在onMeasure中只执行一次的判断
    private Boolean once = false;

    public SlidingButtonView(Context context) {
        this(context, null);
    }

    public SlidingButtonView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);

    }

    public SlidingButtonView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        this.setOverScrollMode(OVER_SCROLL_NEVER);
    }

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

        if (!once) {
            //只需要执行一次
            mTextView_Delete = (TextView) findViewById(R.id.tv_delete);
            rbtn = (RadioButton) findViewById(R.id.rbtn);
            text = (TextView) findViewById(R.id.text);
            once = true;

        }
    }

    //使Item在每次变更布局大小时回到初始位置,并且获取滚动条的可移动距离
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
        if (changed) {
            //获取水平滚动条可以滑动的范围,即右侧按钮的宽度
            mScrollWidth = mTextView_Delete.getWidth();
            leftWidth = rbtn.getWidth();

            this.scrollTo(leftWidth, 0);
            // LogUtils.d("可以滑动的范围:" + mScrollWidth);

        }
    }

    private boolean canTouch = true;

    public boolean isCanTouch() {
        return canTouch;
    }

    public void setCanTouch(boolean canTouch) {
        this.canTouch = canTouch;
    }

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        if (!canTouch) {
            return true;
        }
         int action = ev.getAction();
        switch (action) {
            case MotionEvent.ACTION_DOWN:
            case MotionEvent.ACTION_MOVE:
                mIonSlidingButtonListener.onDownOrMove(this);
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:

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

    //滚动监听,为了让删除按钮显示在项的背后的效果
    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        //  mTextView_Delete.setTranslationX(l - mScrollWidth -100);
        // mTextView_Delete.setTranslationX(l - mScrollWidth  );
        //this.setX(l);
    }

    public void changeScrollx() {
//        LogUtils.d("getScrollX(): " + getScrollX());
//        LogUtils.d("mScrollWidth: " + mScrollWidth);
//        LogUtils.d("leftWidth: " + leftWidth);
        if (getScrollX()-leftWidth >= (mScrollWidth / 2)) {
            this.smoothScrollTo(mScrollWidth + leftWidth, 0);
            isOpen = true;
            mIonSlidingButtonListener.onMenuIsOpen(this);
        } else {
            this.smoothScrollTo(leftWidth, 0);
            isOpen = false;
        }
    }

    Handler scrollHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what==2) {
                SlidingButtonView.this.smoothScrollTo(leftWidth,0);
            } else if (msg.what==1) {
                SlidingButtonView.this.smoothScrollTo(0,0);
            }
        }
    };

    public void openMenu() {
//        if (isOpen) {
//            return;
//        }
        Message msg = new Message();
        msg.what= 1;
        scrollHandler.sendMessage(msg);

        isOpen = true;
        mIonSlidingButtonListener.onMenuIsOpen(this);
    }

    public void closeMenu() {
        if (!isOpen) {
            return;
        }
        Message msg = new Message();
        msg.what= 2;
        scrollHandler.sendMessage(msg);

        isOpen = false;
    }

    public void setSlidingButtonListener(IonSlidingButtonListener listener) {
        mIonSlidingButtonListener = listener;
    }

    public interface IonSlidingButtonListener {
        void onMenuIsOpen(View view);

        void onDownOrMove(SlidingButtonView slidingButtonView);
    }

}
这个类是重点所在,我们按方法来一个一个讲:

 onMeasure:

进行布局的初始化操作。

onLayout:

获取水平滚动条可以滑动的范围,即右侧按钮的宽度

滑动到初始范围。

setCanTouch:

设置是否响应触摸事件。

onTouchEvent:

点击事件判断,在移动事件:MotionEvent.ACTION_MOVE中执行拖动条的滑动。

在事件取消:MotionEvent.ACTION_CANCEL中执行让滑动条滚动回到原位。


源码下载:

源码下载

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值