RecyclerView源碼分析(一)--整體設計


RecyclerView這個控件出來已經有一段時間了,如果看這篇文章的你,還沒有使用過這個控件。那請先去學習怎樣使用。不然看也白看。這里奉上一些關於介紹RecyclerView使用方法的優秀博客:

這幾篇文章都是介紹RecyclerView使用方法的精品文章,其次還有很多大神做的RecyclerView的第三方庫,這里我就不一一列舉了,自己去按需搜索吧。

然后對於已經對RecyclerView有初步了解的讀者,我們一起步入本文的正題。

RecyclerView的設計目的

研究一個現有的控件,先看看這個控件的設計目的是什么。其實就是看看RecyclerView是干什么的。官方對RecyclerView的介紹是很簡短的一句話:

A flexible view for providing a limited window into a large data set.

一個用來為大量數據集合提供有限窗口的靈活的視圖。我的翻譯有點怪,自己看英文理解。介紹言簡意賅,一針見血...好,我沒詞兒了。

其中關鍵的有兩點:

  • providing a limited window into a large data set
  • flexible

針對與這兩點我們可以看看RecyclerView的整體設計。

RecyclerView整體設計

RecyclerView中,針對要達到的功能點,都有相關的設計,下面分點來分析RecyclerView的設計。

RecyclerView數據展示的設計思路

在上一節中提到的,其設計目的的第一點就是展示大量數據。其實RecyclerView在這一點上和ListView等控件具有相同的設計思路,都是使用了設計模式中的適配器模式。不過即使你不知道適配器模式也不用擔心。

首先呢,來看一張 巨丑無比 但是 簡單明了 的結構圖:

圖一
圖一

首先RecyclerView是一個ViewGroup,它和我們常用的各種Layout一樣,是用來裝很多子View的容器,那么它里面裝的那些View是怎么來的呢?其實是來自ViewHolder中的itemView的。那么ViewHolder是從哪里生成的呢?顯示的數據又是在哪里設置的呢?這就是Adapter的作用,它根據要展示數據的內容和類型,生成相應的ViewHolder,並對相應的View進行設置,從而展示出來。

如果從數據源出發就是,Adapter將要展示的數據根據其內容和類型,轉化成對應的ViewHolder並對其進行設置,然后RecyclerView把ViewHolder中的itemView展示出來。

如果把這個圖套用到適配器模式中,RecyclerView就是其中的Client,ViewHolder就是Target,Adapter自然就是Adapter,Data就是Adaptee。我這個圖沒有嚴格去按照適配器模式中的畫是為了讓即使不知道適配器模式的人也能看懂。

這個結構其實不通過源碼也可以看出,在使用RecyclerView的時候也可以體會到。其中由Data生成對應用於展示的ViewHolder,就是通過實現Adapter中的onCreateViewHolder(ViewGroup parent, int viewType);onBindViewHolder(VH holder, int position);這兩個方法。

設計目的中的第一點我們清楚了,那么我們來看第二點。

RecyclerView flexible的設計思路

在研究和探討這個問題的之前我們需要具體化flexible。那么RecyclerView有哪些地方體現出了flexible?個人拙見有以下幾點:

  1. 布局
  2. 動畫
  3. 裝飾

這些大家基本也都知道。那么我們分別看它在每個功能點上面的設計:

RecyclerView布局策略設計思路

細心的讀者應該在上圖中發現,在ViewHolder到RecyclerView的箭頭上有三個點,其實就是暗示了這其中還有很多的貓膩!

還是先上一張 巨丑無比 但 簡單明了 的圖。

圖二
圖二

RecyclerView布局十分靈活,是因為RecyclerView將自己的布局策略全權交給了LayoutManager。仔細閱讀源碼還可以發現,就連View的添加,都是通過LayoutManager完成的。LayoutManager所做的事情就是拿到ViewHolder中的itemView,然后根據LayoutManager中定義的布局策略,對itemView進行布局,然后添加到RecyclerView中。

因此使用者可以根據自己的需要,自定義布局策略,而這里系統提供好了三種布局策略,線性布局,網格布局和瀑布流布局。一般情況下這三種已經滿足了我們的需求。如果不能,用戶可以自定義布局策略。

RecyclerView動畫過程系統設計思路

RecyclerView作為一種展示大量數據的視圖控件,難免會遇到數據變化的情況。例如添加,刪除,更改等。當這些事情發生的時候,猿人往往喜歡通過動畫來體現這種變化。那么在RecyclerView中便提供了一種非常靈活的動畫機制。

同樣先上一張 巨丑無比 但 簡單明了 的圖。

圖三
圖三

首先,達到數據改變觸發動畫,我們通常使用Adapter中的notifyXXX方法即可。但是其內部是如何工作的呢?

其實notify系列的方法可以看作是發出一個事件,在這里Adapter和RecyclerView的工作原理,是一個典型的觀察者模式。

RecyclerView是觀察者,Adapter是可觀察的,在設置Adapter的時候RecyclerView訂閱觀察事件,當Adapter中的數據發生改變的時候通知RecyclerView。然后RecyclerView接到通知之后進行了很多處理。並觸發重新布局。在布局過程中又經過一系列處理,將這些動畫的信息存儲到ViewInfoStore中。在布局結束的時候由ViewInfoStore統一處理並通過CallBack中的方法調用ItemAnimator中的方法執行動畫。

RecyclerView動畫的靈活性是通過ItemAnimator實現的。各位猿們可以通過繼承ItemAnimator,然后實現里面的方法,來實現各種各樣的動畫效果。

RecyclerView裝飾系統設計思路

這里其實並沒有什么好講的,實現ItemDecoration類中的抽象函數即可。RecyclerView內部就是在onDraw的時候執行ItemDecoration的onDraw,在draw的時候執行ItemDecoration的onDrawOver函數。在計算itemView的padding的時候將getItemOffsets得到的Rect加入其中,從而空出裝飾內容的區域。其靈活性在於程序員們可以自定義ItemDecoration,實現各種各樣的裝飾。

對於ItemDecoration有一篇文章介紹的比較好,在這里推薦給大家。

建林大神的 深入理解 RecyclerView 系列之一:ItemDecoration其中也進行了深入講解,而且我覺的可以了,這部分源碼也沒多少,很簡單。

RecyclerView視圖復用的設計思路

結合前兩節的內容,我們的結構圖應該成這個樣子了(動畫部分於該節無關,省略動畫部分結構圖):

圖四
圖四

其中ViewHolder的那一列很奇怪,是有多少個Data就有多少ViewHolder嗎?ViewHolder是存儲在哪里的?

那么將RecyclerView的復用結構補充上。又一張 巨丑無比 但 簡單明了 的圖。

圖五
圖五

這個相對於圖四多了一個Recycler和RecyclerViewPool。這兩個可能都不熟悉,那么對這兩個類進行一個簡單的介紹:

Recycler

A Recycler is responsible for managing scrapped or detached item views for reuse.

一個Recycler是負責管理成為碎片的視圖或者已經detached的視圖,從而實現View的復用。

RecyclerViewPool

RecycledViewPool lets you share Views between multiple RecyclerViews.

RecycledViewPool可以讓你在多個RecyclerView之間分享視圖

翻譯的不好,不能忍的看原文。

介紹都說的很明白了,還有其實ViewHolder的創建和bind都是由Recycler執行的。還有LayoutManager獲得ViewHolder的itemView,也是通過Recycler提供的。簡單介紹一下Recycler和RecyclerViewPool的內部結構。

  1. Recycler里有幾個ViewHolder的容器,用來存儲不同狀態的ViewHolder,以便之后復用。其中ViewCacheExtension類,是用戶可以自定義復用機制的類。
  2. RecyclerViewPool,這個可以從外部對多個RecyclerView設置同一個RecyclerViewPool,從而實現多個RecyclerView中的ViewHolder的復用。

總結

本章簡單的介紹了RecyclerView內部設計的大體框架結構。接下來會詳細的介紹主要流程和機制。整理源碼很費體力,如果這篇文章讓你有所收獲,請不要吝惜你的喜歡,你的喜歡是我寫作的動力。



作者:柴澤建_Jack
鏈接:https://www.jianshu.com/p/9ddfdffee5d3
來源:簡書
著作權歸作者所有。商業轉載請聯系作者獲得授權,非商業轉載請注明出處。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM