1. 程式人生 > >Android——實現RecyclerView左側滑刪除與右側滑選擇

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中執行讓滑動條滾動回到原位。

原始碼下載:

原始碼下載