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

Posted 化作孤岛的瓜

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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中执行让滑动条滚动回到原位。


源码下载:

源码下载

以上是关于Android——实现RecyclerView左侧滑删除与右侧滑选择的主要内容,如果未能解决你的问题,请参考以下文章

Android 使用HorizontalScrollView实现RecyclerView左滑删除的功能

如何将 ItemDecoration 从左侧和右侧都应用到 RecyclerView 项目?

android recyclerview 的使用限制

基于Android的医院预下单叫号排队系统

RecyclerView双列表联动效果开发细节

RecyclerView双列表联动效果开发细节