1. 程式人生 > >RecyclerView 的簡單使用方法

RecyclerView 的簡單使用方法

1.RecyclerView的使用方法(使用的Android Studio開發)
首先是recyclerview的配置問題,recyclerview是要匯入android.support.v7.widget.RecyclerView的包。還要在Gradle Scripts中新增

compile 'com.android.support:recyclerview-v7:24.0.0'

,然後同步。
2.使用RecyclerView實現的主要是內容是Item的新增和刪除以及ListView,GridView和瀑布流的切換,還有就是Item的長按和短按的點選事件。

ListView和GridView的切換還是很好用,程式碼也很簡單。主要是實現了以下程式碼。

0.接入

build.gradle 檔案中加入

compile 'com.android.support:recyclerview-v7:24.0.0'

1.建立物件

RecyclerView recyclerview = (RecyclerView) findViewById(R.id.recyclerview);

2.設定顯示規則

recyclerview.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));

RecyclerView 將所有的顯示規則交給一個叫 LayoutManager

的類去完成了。

LayoutManager 是一個抽象類,系統已經為我們提供了三個預設的實現類,分別是 LinearLayoutManager GridLayoutManagerStaggeredGridLayoutManager,從名字我們就能看出來了,分別是:線性顯示、網格顯示、瀑布流顯示。當然你也可以通過繼承這些類來擴充套件實現自己的 LayougManager

3.設定介面卡

recyclerview.setAdapter(adapter);

介面卡,同 ListView 一樣,用來設定每個item顯示內容的。

通常,我們寫 ListView 介面卡,都是:

  • 首先繼承 BaseAdapter

  • 實現四個抽象方法;

  • 建立一個靜態 ViewHolder

  • getView() 方法中判斷 convertView 是否為空,建立還是獲取 ViewHolder 物件。

RecyclerView 也是類似的步驟:

  • 首先繼承RecyclerView.Adapter<VH>類;

  • 實現三個抽象方法;

  • 建立一個靜態的 ViewHolder

不過 RecyclerView ViewHolder 建立稍微有些限制,類名就是上面繼承的時候泛型中宣告的類名(或者應該說,上面泛型中的類名需要是這個holder的類名);並且 ViewHolder 必須繼承自RecyclerView.ViewHolder類。

public class DemoAdapter extends RecyclerView.Adapter<DemoAdapter.VH> {
 
 private
List<Data> dataList;
 private Context context;

 public DemoAdapter(Context context, ArrayList<Data> datas) {
   this.dataList = datas;
   this.context = context;  }

 @Override  public VH onCreateViewHolder(ViewGroup parent, int viewType) {
   return new VH(View.inflate(context, android.R.layout.simple_list_item_2, null));  }

 @Override  public void onBindViewHolder(VH holder, int position) {    holder.mTextView.setText(dataList.get(position).getNum());  }

 @Override  public int getItemCount() {
   return dataList.size();
 }
 
 public static class VH extends RecyclerView.ViewHolder {
   
   TextView mTextView;
   
   public VH(View itemView) {
     mTextView = (TextView) itemView.findViewById(android.R.id.text1);
   }  }
}

更多方法

除了常用方法,當然還有不常用的。

瀑布流與滾動方向

前面已經介紹過,RecyclerView實現瀑布流,可以通過一句話設定:recycler.setLayoutManager(new StaggeredGridLayoutManager(2, VERTICAL))就可以了。

其中 StaggeredGridLayoutManager 第一個引數表示列數,就好像 GridView 的列數一樣,第二個引數表示方向,可以很方便的實現橫向滾動或者縱向滾動。

新增刪除 item 的動畫

ListView 每次修改了資料來源後,都要呼叫 notifyDataSetChanged() 重新整理每項 item 類似,只不過 RecyclerView 還支援區域性重新整理notifyItemInserted(index)、
notifyItemRemoved(position)、
notifyItemChanged(position)。

在新增或刪除了資料後,RecyclerView 還提供了一個預設的動畫效果,來改變顯示。同時,你也可以定製自己的動畫效果:模仿 DefaultItemAnimator 或直接繼承這個類,實現自己的動畫效果,並呼叫recyclerview.setItemAnimator(new DefaultItemAnimator()); 設定上自己的動畫。

LayoutManager的常用方法

  • findFirstVisibleItemPosition() 返回當前第一個可見 Item 的 position

  • findFirstCompletelyVisibleItemPosition() 返回當前第一個完全可見 Item 的 position

  • findLastVisibleItemPosition() 返回當前最後一個可見 Item 的 position

  • findLastCompletelyVisibleItemPosition() 返回當前最後一個完全可見 Item 的 position.

  • scrollBy() 滾動到某個位置。

adapter封裝

其實很早之前寫過一篇關於 RecyclerView 介面卡的封裝,所以這不再贅述了,傳送門:RecyclerView的通用介面卡

吐槽

OnItemTouchListener 什麼鬼?

用習慣了 ListView OnItemClickListenerRecyclerView 你的 OnItemClickListener 呢?

Tell me where do I find, something like ListView listener ?

好吧,翻遍了 API 列表,就找到了個 OnItemTouchListener ,這特麼什麼鬼,我幹嘛要對每個 item 監聽觸控式螢幕事件。

萬萬沒想到,最終我還是在 Google IO 裡面的介紹找到了原因。原來是 Google 的工程師分不清究竟是改給 listview 的 item 新增點選事件,還是應該給每個 item 的 view 新增點選事件,索性就不給 OnItemClickListener 了,然後在 support demo 裡面,你就會發現,RecyclerView 的 item 點選事件都是寫在了 adapter 的 ViewHolder 裡面。

當然,除了 support demo 包裡面使用的在 ViewHolder 裡面設定點選事件以外,我還寫好了一個 RecyclerView 使用的 OnItemClickListener 程式碼請見:RecyclerItemClickListener.java

需要一提的是,網上有很多這種類似的 ItemClickListener ,在使用的時候一定注意一個問題,就是迴圈引用問題。比如 listener 裡面持有了一個 recyclerview, 而這個 recyclerview 在呼叫 setListener() 的時候又持有了一個 listener。儘管 Java 虛擬機器現在可以解決這種問題了,但作為程式碼編寫者,這種寫法還是應該儘量避免的。

divider 跑哪了?

ListView中設定 divider 非常簡單,只需要在 XML 檔案中設定就可以了,同時還可以設定 divider 高度。

android:divider="@android:color/black"
android:dividerHeight="2dp"

而在RecyclerView裡面,想實現這兩種需求,稍微複雜一點,需要自己繼承RecyclerView.ItemDecoration來實現想要實現的方法。

雖說這樣寫靈活多了,但是要額外寫一個類去做難免麻煩,這裡大家可以看我已經實現好的一個封裝,包括顯示純色divider顯示圖片dividerdivider的上下左右的間距寬高設定 應該可以滿足基本需求了:Divider.java

使用 demo 可以檢視:Github 【自定義 Divider 使用

LayoutManager工作原理

java.lang.Object
   ↳ android.view.View
        ↳ android.view.ViewGroup
            ↳ android.support.v7.widget.RecyclerView

首先是 RecyclerView 繼承關係,可以看到,與 ListView 不同,他是一個 ViewGroup。既然是一個 View,那麼就不可少的要經歷 onMeasure()onLayout()onDraw() 這三個方法。

實際上,RecyclerView 就是將 onMeasure()onLayout() 交給了 LayoutManager 去處理,因此如果給 RecyclerView 設定不同的 LayoutManager 就可以達到不同的顯示效果,因為onMeasure()onLayout()都不同了嘛。

ItemDecoration 工作原理

ItemDecoration 是為了顯示每個 item 之間分隔樣式的。它的本質實際上就是一個 Drawable。當 RecyclerView 執行到 onDraw() 方法的時候,就會呼叫到他的 onDraw(),這時,如果你重寫了這個方法,就相當於是直接在 RecyclerView 上畫了一個 Drawable 表現的東西。

而最後,在他的內部還有一個叫getItemOffsets()的方法,從字面就可以理解,他是用來偏移每個 item 檢視的。當我們在每個 item 檢視之間強行插入繪畫了一段 Drawable,那麼如果再照著原本的邏輯去繪 item 檢視,就會覆蓋掉 Decoration 了,所以需要getItemOffsets()這個方法,讓每個 item 往後面偏移一點,不要覆蓋到之前畫上的分隔樣式了。

ItemAnimator

每一個 item 在特定情況下都會執行的動畫。說是特定情況,其實就是在檢視發生改變,我們手動呼叫notifyxxxx()的時候。通常這個時候我們會要傳一個下標,那麼從這個標記開始一直到結束,所有 item 檢視都會被執行一次這個動畫。

Adapter工作原理

首先是介面卡,介面卡的作用都是類似的,用於提供每個 item 檢視,並返回給 RecyclerView 作為其子佈局新增到內部。

但是,與 ListView 不同的是,ListView 的介面卡是直接返回一個 View,將這個 View 加入到 ListView 內部。而 RecyclerView 是返回一個 ViewHolder 並且不是直接將這個 holder 加入到檢視內部,而是加入到一個快取區域,在檢視需要的時候去快取區域找到 holder 再間接的找到 holder 包裹的 View。

ViewHolder

每個 ViewHolder 的內部是一個 View,並且 ViewHolder 必須繼承自RecyclerView.ViewHolder類。
這主要是因為 RecyclerView 內部的快取結構並不是像 ListView 那樣去快取一個 View,而是直接快取一個 ViewHolder ,在 ViewHolder 的內部又持有了一個 View。既然是快取一個 ViewHolder,那麼當然就必須所有的 ViewHolder 都繼承同一個類才能做到了。

快取與複用的原理

還是一張截圖

RecyclerView 的內部維護了一個二級快取,滑出介面的 ViewHolder 會暫時放到 cache 結構中,而從 cache 結構中移除的 ViewHolder,則會放到一個叫做 RecycledViewPool 的迴圈快取池中。

順帶一說,RecycledView 的效能並不比 ListView 要好多少,它最大的優勢在於其擴充套件性。但是有一點,在 RecycledView 內部的這個第二級快取池 RecycledViewPool 是可以被多個 RecyclerView 共用的,這一點比起直接快取 View 的 ListView 就要高明瞭很多,但也正是因為需要被多個 RecyclerView 公用,所以我們的 ViewHolder 必須繼承自同一個基類(即RecyclerView.ViewHolder)。

預設的情況下,cache 快取 2 個 holder,RecycledViewPool 快取 5 個 holder。對於二級快取池中的 holder 物件,會根據 viewType 進行分類,不同型別的 viewType 之間互不影響。

原始碼分析