溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Android自定義滑動刪除效果的實現代碼

發布時間:2020-08-25 05:11:15 來源:腳本之家 閱讀:244 作者:流浪遠方YP 欄目:移動開發

先給大家展示下效果圖,如果感覺不錯,請參考實現代碼:

Android自定義滑動刪除效果的實現代碼

序言

最近項目中需要用到滑動刪除,然后去網上搜了一下,發現現有網上的各種解決辦法各式各樣,但是還是找不到一個能將所有細節和邏輯處理好的,至于滑動刪除部分,我覺得處理的相對比較好的是 QQ(包括處理各種邏輯和細節);最終,苦尋無果,于是決定自己動手,豐衣足食

這篇文章將從現有 Android 滑動刪除的痛點,到搭建好一個基本的框架,到最終提供一份完整的 Demo為止,爭取為讀者提供最大的可定制化

正文

一. 滑動刪除的痛點

(1). 現有資料中的不足

筆者參閱了網上的一些博客,發現,這些博客中大多能夠基本實現滑動刪除,但是存在的問題是,對于面向用戶實際使用而言,卻是遠遠不夠的大多數博客實現的只是當手指 DOWN 的時候,通過判斷左右滑動和上下滑動的距離之比來判斷 Item 是否應該滑動;但是有一個問題就是,用戶 DOWN 的時候獲得焦點的 Item ,但是 MOVE 的時候手指離開了該 Item 的時候應該如何處理呢? 按照正常的用戶邏輯,這時仍然應該是該 Item 處理滑動事件最重要和最難的部分當然也是滑動沖突了,即不管使用 RecyclerView 還是使用 ListView 實現,其都存在處理上下滑動和左右滑動的沖突問題,很明顯的是我們不能一味地攔截所有事件,因為對于上下滑動事件還需要交給 RecyclerView/ListView 來實現正常的上下滑動;滑動沖突部分如果處理不好的話會出現很明顯的卡頓現象,同時也會出現不符合用戶心理預期的響應,而這些都是用戶不友好的

另外,現有的資料都是在自己的代碼實現上講解的,對于實現正真的定制化還是很有難度的,當我們想要實現自己想要的功能時,我們還需要去看懂一些不相關的處理邏輯

(2). 需要處理的細節

我一直覺得 QQ 在處理滑動刪除上做的是相對比較好的,特別是從各種細節處理上,它基本上都能給出符合用戶心理預期的響應,這里也是以 QQ 為例來介紹幾種需要注意和處理的細節;當然,需要注意的地方很多,一一例舉不太現實,具體的還是需要自己動手啦

側滑過程中,DOWN 時得到焦點的 Item 在 MOVE 過程中失去了焦點應該怎么處理?(即對應上面的 現有資料中的不足 中的第2項);如下圖所示,手指 DOWN 的時候得到焦點的是 Item 7, 但是之后手指在 MOVE 過程中,Item 7 失去了焦點;正如上面所說,此時還是應該交由該 Item 7 處理滑動事件(如果在 DOWN 的時候已經判為側滑的話)

Android自定義滑動刪除效果的實現代碼 

如果當前有 Item 正在側滑,那么 RecyclerView 就不能再同時上下滑動
如果當前有 Item 處于打開狀態,那么在下一次 DOWN 的時候應該先將其關閉,同時在 UP 之前,MOVE 事件都應該是無效的(對于這種情況,也可以按照自己的邏輯處理,如: 如果當前有 Item 處于打開狀態,那么在下一次 DOWN 的時候應該先將其關閉,但是在關閉之后,在 UP 之前出現的 MOVE 事件也應該響應)
在一次 DOWN->MOVE...MOVE->UP 的完整過程中,一旦初始判斷決定了應該是上下滑動或者 Item 的左右滑動之后,在 MOVE 過程中就不能改變,直至下一次新的判斷過程為止(這種情況容易出現在用戶在一次過程中反復的上下滑動時突然來一次左右滑動(或者反復的左右滑動過程中,突然來一次上下滑動))

二. 一個框架

(1). 使用 RecyclerView 搭建框架

1. 預備知識

RecyclerView 對外提供的接口已經比較完善,所以不需要再去繼承 RecyclerView 來監聽其 MotionEvent 事件
可以通過 RecyclerView 的 addOnItemTouchListener() 方法來實現對所有 MotionEvent 的攔截,其需要傳入一個 RecyclerView.OnItemTouchListener 對象,這是一個 interface ,需要我們自己來實現邏輯,這里筆者寫了一個大致的 Demo 先來看看其各個方法之間的聯系

recyclerView.addOnItemTouchListener(new  RecyclerView.OnItemTouchListener() {
     @Override
     public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
       switch (e.getAction()) {
         case MotionEvent.ACTION_DOWN: {
           Log.d("@HusterYP", String.valueOf("onInterceptTouchEvent DOWN"));
           break;
         }
         case MotionEvent.ACTION_MOVE: {
           Log.d("@HusterYP", String.valueOf("onInterceptTouchEvent MOVE"));
           break;
         }
         case MotionEvent.ACTION_UP: {
           Log.d("@HusterYP", String.valueOf("onInterceptTouchEvent UP"));
           break;
         }
       }
       return true;
     }
     @Override
     public void onTouchEvent(RecyclerView rv, MotionEvent e) {
       switch (e.getAction()) {
         case MotionEvent.ACTION_MOVE: {
           Log.d("@HusterYP", String.valueOf("onTouchEvent MOVE"));
           break;
         }
         case MotionEvent.ACTION_UP: {
           Log.d("@HusterYP", String.valueOf("onTouchEvent UP"));
           break;
         }
       }
     }
     @Override
     public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) {
     }
   });

關于該 Demo 的代碼可至筆者 Github 上下載執行測試;這里筆者就直接給出在 onInterceptTouchEvent 方法中返回不同值時的結論了:

如果在最后返回 false,那么 DOWN,MOVE,UP事件都是交給 onInterceptTouchEvent 處理可上下滾動
如果在最后返回 true,那么 onInterceptTouchEvent 只會接受到一個 DOWN,一個 MOVE;但是onTouchEvent 接收到剩下的 MOVE 和 UP; 不可上下滾動

如果最后返回 false,但是在 onInterceptTouchEvent 的 DOWN 判斷中返回 true,這種情況同1
如果最后返回 false 或者 true,但是在 onInterceptTouchEvent 的 DOWN 判斷中調用rv.setLayoutFrozen(true);方法,那么 onInterceptTouchEvent 只會收到一個 DOWN

如果在最后返回 false,但是在 onInterceptTouchEvent 的 MOVE 判斷中 return true;的話,同情況2
那么通過上面的預備知識和結論,我們實現的滑動刪除的思路也就漸漸清晰了:

最關鍵的是如何判斷應該是 Item 的橫向滑動還是 RecyclerView 的上下滑動,這里可以通過判斷手指滑動的速度來判斷: 即在 onInterceptTouchEvent 方法中的 MOVE 事件中去判斷,如果 x 向速度大于 y 向速度,那么可以判斷為是 Item 的橫向滑動,直接 return true 即可,正如上面分析的那樣,之后直接在 onTouchEvent 方法中處理 Item 的滑動邏輯即可;這里還有一點需要注意的是,在 onInterceptTouchEvent 的 MOVE 事件中判斷時,對于一個完整的 DOWN->MOVE...MOVE->UP 過程,其實只需要,也只能執行一次判斷,因為對于這樣一個完整的過程,一旦在初始 MOVE 中將該過程判斷為 Item 左右滑動或者 RecyclerView 上下滑動之后,中間就不可能突然改變,這對應上面 需要處理的細節 中的情況5;所以這里筆者是通過一個標志變量(flag)來實現的,需要注意的是在 UP 之后需要把 flag 置位,方便下一次判斷

對于當手指 DOWN 時,已經有了一個 Item 處于打開狀態,那么此時也應該分情況,當此時手指 DOWN 處仍然為該打開 Item 時,那么手指的移動情況就應該交給該 Item 來處理;如果此時手指 DOWN 的位置不是該打開 Item ,那么合理的處理是先關閉該 Item,之后在該過程中的 MOVE 事件還要不要響應,其實筆者覺得都是可以接受的;至于具體的細節處理是設置兩個 ViewHolder 變量來記錄(curHolder和oldHolder)即可,可在 onInterceptTouchEvent 中的 DOWN 事件中判斷

至于 Item 的平滑滑動和添加各種動畫之類的,讀者可以自行決定,這個不是本文的重點

三. 一個可擴展的Demo

這里給出筆者實現的一個完整 Demo,代碼中也有部分注釋,可以結合本文再來理清一下邏輯
完整Demo代碼可以到筆者 Github 下載

同時,讀者也可以根據自己的實際需要,重新設置布局和重新添加一些自己的滑動邏輯;需要需要解釋的是,這里筆者為了實現平滑移動,所以繼承了 RelativeLayout 在實現了一個 MyRelativeLayout 類,即最外層布局,如下可知,筆者只是簡單的在其中使用了一個 Scroller 類來實現平滑移動,其他也沒有復雜的操作

public class MyRelativeLayout extends RelativeLayout {
private Scroller scroller;
 public MyRelativeLayout(Context context) {
   super(context);
   init(context);
 }
 public MyRelativeLayout(Context context, AttributeSet attrs) {
   super(context, attrs);
   init(context);
 }
 public MyRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
   super(context, attrs, defStyleAttr);
   init(context);
 }
 @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
 public MyRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
   super(context, attrs, defStyleAttr, defStyleRes);
   init(context);
 }
 private void init(Context context) {
   scroller = new Scroller(context);
 }
 public void onScroll(int dx) {
   if (this.getScrollX() != 0) {
     scroller.startScroll(this.getScrollX(), 0, dx, 0);
     invalidate();
   }
 }
 @Override
 public void computeScroll() {
   super.computeScroll();
   if (scroller.computeScrollOffset()) {
     this.scrollTo(scroller.getCurrX(), 0);
     invalidate();
   }
 }
}

總結

以上所述是小編給大家介紹的Android自定義滑動刪除效果的實現代碼,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對億速云網站的支持!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

亚洲午夜精品一区二区_中文无码日韩欧免_久久香蕉精品视频_欧美主播一区二区三区美女