RecyclerView 梳理:點擊&長按事件、分割線、拖曳排序、滑動刪除

轉載大牛鏈接

這次主要是把 RecyclerView 比較常用的基本的點,在這裏集中整理一下。從這篇文章主要梳理以下幾點:


  • 優雅的實現:item 點擊事件 & item 長點擊事件

  • RecyclerView 添加 divider 的標準姿勢

  • RecyclerView 實現 item 的拖曳排序和滑動刪除

  • 拖曳排序時,限制首個 item 固定的實現


先看一下最終的效果圖:



--swipe and drag--



--drag--


自從 RecyclerView 發佈以來,由於其高度的可交互性被廣泛使用。相信大家肯定對它的使用方法已經非常熟練了,今天主要是爲大家總結一下較正常用法更加優雅的方式。


如果你想再回顧一下 RecyclerView 的基本使用方法,推薦鴻洋的這篇文章:
《Android RecyclerView 使用完全解析 體驗藝術般的控件》

http://blog.csdn.net/lmj623565791/article/details/45059587


1item 點擊事件 & item 長點擊事件


使用方式


RecyclerView 的 api 雖然沒有提供 onItemClickListener 但是提供了 addOnItemTouchListener() 方法,既然可以添加觸摸監聽,那麼我們完全可以獲取觸摸手勢來識別點擊事件,然後通過觸摸座標來判斷點擊的是哪一個item。




其中 OnRecyclerItemClickListener 是自定義的一個觸摸監聽器,代碼如下:




GestureDetectorCompat 中傳入了一個 ItemTouchHelperGestureListener,代碼如下:



原理分析


上面的代碼很簡單沒什麼複雜的地方,就是通過一個手勢探測器 GestureDetectorCompat 來探測屏幕事件,然後通過手勢監聽器 SimpleOnGestureListener 來識別手勢事件的種類,然後調用我們設置的對應的回調方法。


這裏值得說的是:當獲取到了 RecyclerView 的點擊事件和觸摸事件數據 MotionEvent,那麼如何才能知道點擊的是哪一個 item 呢?


RecyclerView已經爲我們提供了這樣的方法:findChildViewUnder()


我們可以通過這個方法獲得點擊的 item ,同時我們調用 RecyclerView 的另一個方法getChildViewHolder(),可以獲得該 item 的 ViewHolder,最後再回調我們定義的虛方法 onItemClick() 就ok了,這樣我們就可以在外部實現該方法來獲得 item 的點擊事件了。


2添加 divider 的標準姿勢


當你想給條目間添加 divider 時,你可能自然而然的去嘗試這種方式:


<android.support.v7.widget.RecyclerView
    android:divider="#ffff0000"
    android:dividerHeight="10dp"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />


其實 RecyclerView 是沒有這兩個屬性的,就算你寫上也不會有任何效果。

當然你還可以通過給 item 的最外層佈局設置一個 margin 值,甚至你還可以專門在 item 佈局中的適當地方添加一個高度/寬度爲 1 的帶背景的 View 作爲 divider,這兩種方法呢,確實有效果,但是不夠優雅,有時還可能帶來一些想不到的問題。


其實官方還是爲我們提供了爲 RecyclerView 添加分割線的方式的,那就是方法:mRecyclerView.addItemDecoration() 


該方法的參數爲 RecyclerView.ItemDecoration,該類爲抽象類,且官方目前並沒有提供默認的實現類,我們只能自己來實現。


注意:作者這裏給出了列表佈局和網格佈局分別實現分割線的實現類,由於代碼太長,可以跳轉到原文查看。


上面給出的兩個實例都是最簡單的一條線的分割。這裏的分割線你是可以自由的去自定義它的,具體如何實現也不是太複雜,這裏不再做詳細介紹了,推薦一篇文章:


《RecyclerView之ItemDecoration 講解及高級特性實踐》

http://www.10tiao.com/html/227/201705/2650239745/1.html


3實現 item 的拖曳排序和滑動刪除


下面就主要爲大家梳理一下拖曳排序和滑動刪除的實現,具體實現效果看文章首部效果圖,這裏就不再重複放圖了。


實現方式


主要就要使用到 ItemTouchHelper,ItemTouchHelper 一個幫助開發人員處理拖拽和滑動刪除的實現類,它能夠讓你非常容易實現側滑刪除、拖拽的功能。


(ItemTouchHelper 的使用並不僅僅侷限於 RecyclerView 的滑動刪除,你同意可以用在其他需要拖曳滑動的地方。當然,今天我們不涉及其他地方的使用)


實現的代碼並關聯到 RecyclerView 非常簡單,代碼如下:


ItemTouchHelper itemTouchHelper 
        = new ItemTouchHelper(new ItemTouchHelper.Callback());
itemTouchHelper.attachToRecyclerView(mRecyclerView);


代碼很簡單,沒什麼好說的。


需要我們關注的是創建 ItemTouchHelper 時傳入的參數  ItemTouchHelper.Callback() 。ItemTouchHelper 會在拖拽的時候回調 Callback 中相應的方法,我們只需在 Callback 中實現自己的邏輯。


自定義一個類繼承實現 ItemTouchHelper.Callback 接口,需要實現以下方法:




getMovementFlags() 用於設置是否處理拖拽事件和滑動事件,以及拖拽和滑動操作的方向,有以下兩種情況:


  • 如果是列表類型的 RecyclerView,拖拽只有 UP、DOWN 兩個方向

  • 如果是網格類型的則有 UP、DOWN、LEFT、RIGHT 四個方向


該方法需要編寫的代碼如下:




dragFlags 是拖拽標誌,
swipeFlags 是滑動標誌,
swipeFlags 都設置爲0,暫時不考慮滑動相關操作。


如果設置了相關的 dragFlags,那麼當長按 item 的時候就會進入拖拽並在拖拽過程中不斷回調 onMove() 方法,我們就在這個方法裏獲取當前拖拽的 item 和已經被拖拽到所處位置的 item 的ViewHolder,有了這2個 ViewHolder,我們就可以交換他們的數據集並調用 Adapter 的notifyItemMoved 方法來刷新 item。




只要重寫完上面這兩個方法,RecyclerView 就能實現拖曳的效果了。是不是很簡單?


但是雖然拖曳是沒什麼問題了,但是並不能達到下圖的效果,因爲你正在拖曳的 item 並沒有陰影效果。



那怎麼才能實現被拖曳的 item 有背景顏色加深起到強調的視覺效果呢?這是需要重寫下面兩個方法:




這樣就能完全達到上面圖片的效果了。


滑動刪除


如何實現滑動刪除呢?我們只需要實現第三個方法 onSwipe() 就行了。


代碼如下:




同時也不要忘了修改一下 getMovementFlags() 方法,以便能夠相應滑動事件:




那目前你就能完美的實現拖曳排序和滑動刪除了。


拖曳排序,首個固定


有時我們希望首個 item 不能被拖曳排序。比如我們在新聞 App 中常見當我們進行新聞分類時,“熱門”新聞這個分類總是第一個且不能被拖曳修改,類似下面的效果:




那麼怎麼才能達到上面的效果呢?在上面我們的 Callback 類中有一個方法:


public boolean isLongPressDragEnabled() {
    return true;
}


這個方法是爲了告訴 ItemTouchHelper 是否需要 RecyclerView 支持長按拖拽,默認返回是 ture,理所當然我們要支持,所以我們沒有重寫,因爲默認true。


但是這樣做是默認全部的item都可以拖拽,怎麼實現部分item拖拽呢,在 isLongPressDragEnabled 方法的源碼中有提示說,如果想自定義拖曳 view,那麼就使用 startDrag(ViewHolder) 方法。


第一步:那麼我們就先重寫 isLongPressDragEnabled() 方法,返回 false 讓它控制所有的 item 都不能拖曳。


public boolean isLongPressDragEnabled() {
    return false;
}


第二步:我們給 RecyclerView 設置 item 的長按監聽事件,然後判斷這個 item 是不是第一個(或者最後一個,如果你不想讓最後一個被拖曳的話),如果不是我們就手動調用 startDrag(ViewHolder) 讓 item 開始被拖曳。


結合上面我們提供的給 item 設置點擊和長按事件的方法,我們可以這樣:




第三步:如果你以爲上面兩步你就達到首個 item 固定不被拖曳的話,恭喜你,答對了!首個 item 確實固定不能被拖曳了,可是看看下圖,就會令你大跌眼睛:




雖然我們通過上面兩步控制了首個 item 不能被長按拖曳,但是我們並沒有處理,別的 item 被拖曳到首個 item 的情況。那麼如何才能讓首個 item 不被擠掉呢,這個也很簡單,只需要在 Callback 的 onMove() 方法中處理首個 item 被當着目標 item 的情況就行了。




好了,到這裏就大功告成了。


本文源代碼地址:

https://github.com/OCNYang/RecyclerViewEvent


參考文章:
http://chuansong.me/n/400690551872  
http://chuansong.me/n/400690851058  
http://www.10tiao.com/html/227/201705/2650239745/1.html


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章