對於RecyclerView
的學習,主要是須要掌握如下幾點:html
Adapter
LayoutManager
ItemAnimator
ItemDecorator
ItemTouchHelper
要理解整個RecyclerView
的思想,有一個視頻是必定要看的:RecyclerView ins and outs - Google I/O 2016。今天,咱們就經過這個視頻,把上面所學到的東西串聯起來。git
RecyclerView
RecyclerView
誕生的目的就是爲了替代ListView
,咱們先總結一下在使用ListView
過程中所遇到的問題:程序員
Item
須要編寫不少的代碼 在使用ListView
的時候,有經驗的程序員必定會告訴你在getView
中要這麼寫,若是忘了,那麼會產生很嚴重的性能問題。if (convertView == null) {
//經過LayoutInflator生成convertView,併產生一個ViewHolder,經過setTag關聯起來.
} else {
//經過getTag獲取ViewHolder,進行更新操做.
}
複製代碼
Item
有焦點時,Item
的子控件就沒法獲取到焦點;而若是子控件搶奪了焦點,那麼Item
的點擊事件又不能響應,這個相信你們都遇到過。API
ListView
中提供了不少的API
,可是這些API
又和View
的一些API
重複了,例如咱們能夠給ListView
設置setOnItemClickListener
,也能夠在getView
中給某個View
設置setOnClickListener
,這就讓人很疑惑,到底應當選用哪一個。ListView
中進行添加、刪除、移動等操做的時候,若是但願加上動畫,那麼是很困難的,根本緣由是咱們是經過Adapter
通知ListView
進行更新,然而ListView
根本就無法肯定究竟是哪些View
發生了變化。ListView
在佈局是規整的列表的時候能知足大多數人的使用,然而若是想要實現像瀑布流這種複雜的佈局,而且保證View
可以複用,那麼須要編寫不少的代碼。若是以前有了解過RecyclerView
的基本用法,那麼你會發現,對於上述這些問題,它都給出了本身的解決方案:github
ViewHolder
,提供了onCreateViewHolder
和onBindViewHolder
這兩個方法,把建立View
和綁定View
的操做分離開。onItemClickListener
,以及一些重複的API
。Adapter
中增長了notifyItemChanged()
等方法,讓咱們能夠指定變化的類型和範圍,而且提供了setItemAnimator()
方法,讓開發者可以方便地定義添加、刪除、移動的動畫。LayoutManager
當中,並預製了瀑布流佈局。瞭解了這些,咱們就能知道RecyclerView
能幫咱們解決什麼問題,也就能更好地理解它爲何要這麼設計,下面就開始進入真正的RecyclerView
的學習。緩存
RecyclerView
架構RecyclerView
體系包含三大組件:
LayoutManager
:position the view
ItemAnimator
:animate the view
Adapter
:provide the view
這三大組件各司其職,而RecyclerView
負責管理,就組成了整個RecyclerView
的架構。bash
LayoutManager
LayoutManager
須要負責如下幾部分的工做:架構
Position
它負責View
的擺放,能夠是線性、宮格、瀑布流式或者任意類型,而RecyclerView
不知道也不關心這些,這是LayoutManager
的職責。Scroll
對於滾動事件的處理,RecyclerView
負責接收事件,可是最終仍是由LayoutManager
進行處理滾動後的邏輯,由於只有它在知道View
具體擺放的位置。Focus traversal
當焦點轉移致使須要一個新的Item
出如今可視區域中時,也是由LayoutManager
處理的。Adapter
Adapter
須要負責如下幾部分的工做:app
View
和ViewHolder
,後者做爲整個複用機制的跟蹤單元。Item
和ViewHolder
進行綁定,並存儲相關的信息。RecyclerView
數據變化,支持局部的更新,在提升效率的同時也有效地支持了動畫。Item
點擊事件的處理。ViewHolder
的生命週期LayoutManager
請求RecyclerView
提供指定position
的View
ViewHolder
是和View
相綁定的,同時它也是整個複用框架的跟蹤單元。在RecyclerView
體系中,對ViewHolder
採用了二級緩存,分爲Cache
和Recycled Pool
,當LayoutManager
向RecyclerView
請求位於某個Position
的View
時,Recycled View
會先去Cache
中尋找,若是找到,那麼直接返回;若是找不到,那麼再去Recycled Pool
中尋找,下面就是整個尋找過程的幾種狀況:框架
Cache
這種狀況下,不會調用Adapter
的onCreateViewHolder
或者onBindViewHolder
方法:
Cache
不存在,Recycled Pool
也不存在 這種狀況下,會調用Adapter
的onCreateViewHolder
方法,讓它提供一個對應viewType
的ViewHolder
,咱們在其中創建ViewHolder
和View
之間的關聯。
Cache
不存在,Recycled Pool
存在 這種狀況下,會回調Adapter
的onBindViewHolder
方法,咱們在其中使用當前的數據集合來更新ViewHolder
所綁定的itemView
的狀態。
LayoutManager
找到對應位置的View
LayoutManager
經過addView
方法把以前找到的View
添加進RecyclerView
,RecyclerView
經過onViewAttachToWindow(VH viewHolder)
方法,通知Adapter
這個viewHolder
所關聯的itemView
已經被添加到了佈局當中, ide
LayoutManager
請求RecyclerView
移除某一個位置的View
當LayoutManager
發現再也不須要某一個position
的View
時,它會通知RecyclerView
,RecyclerView
經過onViewDetachFromWindow(VH viewHolder)
通知Adapter
和它綁定的itemView
被移出了。同時,RecyclerView
判斷它是否可以被緩存,假設可以被緩存,那麼它會先被放到Cache
當中,在Cache
中又會判斷它內部是否有須要轉移到Recycled Pool
中的ViewHolder
,在放入以後回收池後,經過onViewRecycled(VH viewHolder)
方法通知Adapter
它被回收了。
在上面的普通的狀況中,onViewDetachFromWindow(VH viewHolder)
是當即被回調的。然而在實際當中,因爲咱們須要對View
的添加、刪除作一些過分動畫,這時候,咱們須要等待ItemAnimator
進行完動畫操做以後,才作detach
和recycle
的邏輯,這一過程對於LayoutManager
是不可見的。
ViewHolder
的銷燬在通常狀況下,咱們不會去銷燬ViewHolder
,而是把它放入到緩存當中,除非出現如下兩種狀況。
ViewHolder
所綁定的itemView
當前狀態異常在放入Recycled Pool
時,會去檢查itemView
的狀態是否正常。這一操做的目的主要是爲了不出現諸如此類的狀況:當前itemView
正在執行動畫,此時它可能呈現半透明的狀態,若是此時把它放入到回收池中,那麼當另外一個位置的position
須要複用它時就可能會出現問題。 當出現上面的狀況後,Recycled Pool
會先經過Adapter
的onFailedToRecycled(VH viewHolder)
告訴它咱們如今出現了異常的狀況,由Adapter
的實現者經過返回值來決定是否仍然要把它放入到Recycled Pool
,默認是返回false
,也就是不放入,那麼這個ViewHolder
就會被銷燬了。
Recycled Pool
中已經沒有足夠的空間Recycled Pool
的空間並非無限大的,所以,若是沒有足夠的空間存放要被回收的ViewHolder
,那麼它也會被銷燬。
notifyItemRangeChanged(0, getItemCount())
方法,這時候爲了進行漸出漸進的動畫,那麼咱們就須要建立兩倍的
ViewHolder
,出現這種狀況時通常有兩種解決方法:
Item
pool.setMaxRecycledViews(type, count)
改變回收池的大小。ItemAnimator
對於Item
的動畫,主要有如下幾種狀況:
Fade In
Fade Out
Translate
Cross Fade
RecyclerView
對於動畫的處理採用了Predictive
的方式,除了當前已經在RecyclerView
佈局中的View
(實線框部分),它還須要知道在屏幕意外的信息(虛線框部分),這樣在H
被刪除的時候,它纔可以對J-K
進行上移動畫,並把原來不在屏幕內的L
上移到可視範圍以內。
ChildHelper
和AdapterHelper
ChildHelper
對於ChildHelper
的做用是:Provide a virtual children list to layoutmanager
,下面咱們就首先看一下爲何須要它。
咱們看下面這種狀況,假如LayoutManager
想要移除一個View
,而ItemAnimator
又但願給這一移除的操做增長一個動畫,那麼這時候就會產生衝突,到底應該怎麼辦,爲此,RecyclerView
經過ChildHelper
來把它們隔離開。
當RecyclerView
收到LayoutManager
要求改變佈局的請求時,它並非直接去更改ViewGroup
,而是讓ChildHelper
和ItemAnimator
去協調,並由它來操做ViewGroup
。
0,1,2,3
,此時咱們移除了
position=0
的
Item
,這時候假如刪除的動畫尚未完成,那麼
LayoutManager
和
RecyclerView
的
getChildAt(0)
返回值將會不一樣,由於在
LayoutManager
並不清楚
ChildHelper
的存在,在它看來,
position=0
的
Item
已經被移除了。
layoutManager.getChildAt(0); //return 1;
recyclerView.getChildAt(0); //return 0;
複製代碼
AdapterHelper
而AdapterHelper
所解決的問題和ChildHelper
相似,ChildHelper
是處理View
的,而AdapterHelper
用來跟蹤ViewHolder
的,其做用爲:
Tracks ViewHolder positions
Virtual Adapter for LayoutManager
提及來可能比較抽象,咱們用下面這種圖理解一下,當咱們移動某個Item
而且它的onLayout
方法尚未完成,那麼Adapter
和Layout
的postion
是不相同的:
ItemDecoration
ItemDecoration
用來在RecyclerView
的Canvas
上進行額外的繪製操做,咱們不只能夠在單個Item
(例如給每一個Item
添加分割線)的Canvas
上進行繪製,也能夠在整個RecyclerView
的Canvas
上進行繪製,此外,咱們還能夠指定Item
之間的間隔:
Custom Drawing on RecyclerViews Canvas
Add offset to View bounds
Have multiple ItemDecoration
須要注意的點:
Do not try to access to adapter
Keep necessary information in viewHolder
General onDraw rules apply
recyclerView.getChildViewHolder(View view)
參考文章:Android RecyclerView 使用徹底解析 體驗藝術般的控件。
RecycledViewPool
RecyclerViewPool
用來緩存那些回收的View
,這些緩存不只能夠提供給單個RecyclerView
使用,還能夠提供和別的自定義控件共享。
Sanctuary for reserve ViewHolders
Can be shared between RecyclerViews or Custom ViewGroups
PerActivity Context
ItemTouchHelper
以前使用ListView
的時候,若是須要支持側滑刪除、拖動排序這種操做,那麼咱們通常用引入一些開源庫,如今RecyclerView
已經幫咱們提供了實現的接口,經過重寫ItemTouchHelper
的方法,就能夠實現上面提到的那些操做。
Drag & Drop
Swipe to dismiss
參考文章:RecyclerView 進階:使用 ItemTouchHelper 實現拖拽和側滑刪除
Tips
onBind Position != final
,use holder.getAdapterPostion()
若是咱們像下面這樣,在onBindViewHolder
中綁定了監聽:public void onBindViewHolder(final ViewHolder, final int position) {
holder.itemView.setOnClickListener(new View.onClickListener) {
@Override
public void onClick(View view) {
removeAtPostion(position);
}
}
}
複製代碼
因爲Item
會被添加、刪除、移動,所以,咱們在onBindViewHolder
中得到位置,並不必定是當前的位置,例如像下面這樣:
onBindViewHolder(holder, 5);
notifyItemMoved(5, 15);
holder.itemView.callOnClick();
複製代碼
那麼就會獲得錯誤的位置,這時候應當使用holder.getAdapterPostion()
來保證可以獲得預期的結果。
Payloads
經過onBindViewHolder
中的List payloads
,咱們能夠指定在bind
的時候只更新某一部分的信息,而不是所有更新。onCreate means create
在onCreateViewHolder
中,始終應當返回一個新的ViewHolder
,而不是返回一個緩存的ViewHolder
。Adapter position and Layout position
就像咱們前面在AdapterHelper
中討論的那樣,在某些時刻,Adapter Position
和Layout Position
並不相等,咱們應當根據狀況選擇須要使用哪一個,Adapter Position
數據所處的位置,而Layout Position
則對應當前**View
的所處的位置**。