Android——實現RecyclerView左側滑刪除與右側滑選擇
阿新 • • 發佈:2019-02-15
專案中要實現的功能,之前找了很久發現網上大部分的側滑刪除和列表全選都是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實現:
這裡沒有太多要說的,主要就是一個判斷是否開啟左側編輯選單的識別符號boolean值allopen的判定方法,其他都為基礎的RecyclerView的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); } }
在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中執行讓滑動條滾動回到原位。
原始碼下載: