作為一個很重要的view,recyclerview內部實現也是很複雜的,下面簡單概括以及總結一下一些比較重要的方法以及實現的思路
關鍵性幾個方法以及變數
- dispatchLayoutStep1
- dispatchLayoutStep2
- dispatchLayoutStep3
- ViewInfoStore
- ViewInfoStore.ProcessCallback
dispatchLayoutStep1
第一步負責把舊的viewholder的資訊記錄下來,包括position,top,left等位置的資訊,封裝成
ItemHolderInfo
作為一個成員放置到ViewInfoStore.InfoRecord
中的preInfo
變數然後放到ViewInfoStore
中, 具體可以檢視addToAppearedInPreLayoutHolders
/addToPreLayout
方法
dispatchLayoutStep2
第二步負責view的佈局,這一步由
layoutmanager
來處理,比如設定的LinearlayoutManager
等
dispatchLayoutStep3
第三步負責動畫的顯示,先是把佈局過後的view的資訊記錄下來,呼叫了
addToPostLayout
方法,封裝成ItemHolderInfo
作為一個成員放置到ViewInfoStore.InfoRecord
中然後放到ViewInfoStore
中,這裡的是記錄在了InfoRecord
的postInfo
中去,最後呼叫this.mViewInfoStore.process(this.mViewInfoProcessCallback);
去開始對view進行動畫操作
這裡step1和step3中分別用一個ItemHolderInfo記錄了佈局前後view的資訊,然後放在了InfoRecord
中的preInfo
以及postInfo
中,這樣一個InfoRecord
就可以記錄一個holder在佈局改變前後的位置資訊,方便後續做動畫上的變化
OnMeasure
這裡的測量分兩種方式,recyclerview有預設的測量策略,layoutmanager可以通過關閉autoMeasure來接管測量的邏輯,但是一般的都是使用預設的測量,
int widthMode = MeasureSpec.getMode(widthSpec);
int heightMode = MeasureSpec.getMode(heightSpec);
this.mLayout.onMeasure(this.mRecycler, this.mState, widthSpec, heightSpec);
boolean measureSpecModeIsExactly = widthMode == 1073741824 && heightMode == 1073741824;
// 表明如果recyclerview有一個確切的寬高,就直接結束流程,否則就要開始下面的邏輯
if (measureSpecModeIsExactly || this.mAdapter == null) {
return;
}
// 如果recyclerview是wrapcontent,寬高無法確定,就需要先去排列view,計算出高度再去設定
// 這裡的state記錄了當前layout進行到了第幾步,詳細可以看下面的介紹
if (this.mState.mLayoutStep == 1) {
this.dispatchLayoutStep1();
}
this.mLayout.setMeasureSpecs(widthSpec, heightSpec);
this.mState.mIsMeasuring = true;
this.dispatchLayoutStep2();
this.mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
if (this.mLayout.shouldMeasureTwice()) {
this.mLayout.setMeasureSpecs(MeasureSpec.makeMeasureSpec(this.getMeasuredWidth(), 1073741824), MeasureSpec.makeMeasureSpec(this.getMeasuredHeight(), 1073741824));
this.mState.mIsMeasuring = true;
this.dispatchLayoutStep2();
this.mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);
}
複製程式碼
RecyclerView.State
這個類封裝了當前RecyclerView的有用資訊。State的一個變數mLayoutStep表示了RecyclerView當前的佈局狀態,包括STEP_START
、STEP_LAYOUT
、 STEP_ANIMATION
S三個,而RecyclerView的佈局過程也分為三步,其中,STEP_START
表示即將開始佈局,需要呼叫dispatchLayoutStep1
來執行第一步佈局,接下來,佈局狀態變為STEP_LAYOUT
,表示接下來需要呼叫dispatchLayoutStep2
裡進行第二步佈局,同理,第二步佈局後狀態變為STEP_ANIMATIONS
,需要執行第三步佈局dispatchLayoutStep3
。所以OnMeasure
如果已經進行了第1,2步,後續dispatchLayout
方法裡只會執行第三步,避免重複測量
Adapter
- AdapterDataObserver
- AdapterDataObservable
adapter中的資料重新整理是通過觀察者模式來觸發的,adapter內有一個AdapterDataObservable
物件,當我們呼叫adapter的noti類方法通知recyclerview重新整理的時候,AdapterDataObservable
會通知觀察者也就是AdapterDataObserver
,而AdapterDataObserver
物件在recyclerview 裡有一個例項化物件RecyclerViewDataObserver
可以檢視內部程式碼,這裡的程式碼就是最後去重新整理的邏輯
1.呼叫Adapterhelper
中的具體方法去判斷是否執行重新整理操作,方法內會記錄當前操作型別是add/move/change還有itemvcount等資訊,一般返回true,如果是notifyDataSetChanged
就直接執行到requestLayout
跳到第7
點
2.triggerUpdateProcessor
3. 執行mUpdateChildViewsRunnable
這個runnable
4. consumePendingUpdateOperations
5. AdapterHelper
的preProcess
去判斷要add還是remove等操作
6. 呼叫AdapterHelper
的preProcess
去回撥到recyclerview
中的對應的方法,然後呼叫offsetPositionsForAdd
等方法,去確認變換過後的viewhodler
的位置資訊,比如add remove 之後其他item的座標等,同時把cachedview
也做同樣的操作
7. 呼叫dispathchLayout
方法,走一遍測繪流程,就是一開始說的三步,這個時候新舊viewholder的資訊被分別記錄起來,最後呼叫this.mViewInfoStore.process(this.mViewInfoProcessCallback);
8. 在這裡面根據viewholder前後資訊,去判斷具體每個view是新增刪除還是移動等操作,回撥到recyclerview裡的mViewInfoProcessCallback
也就是上面的callback,然後run一個runnable,讓recyclerview的ItemAnimator
物件呼叫runPendingAnimations
去開始對view進行動畫操作,具體可以檢視DefaultItemAnimator
裡的實現
notifyDataSetChanged
notifyDataSetChanged會呼叫
processDataSetCompletelyChanged
,方法裡把mDataSetHasChangedAfterLayout
設為了true,後續在step1裡processAdapterUpdatesAndSetAnimationFlags
判斷把mRunSimpleAnimations
和mRunPredictiveAnimations
射程了false
,所以就跳過了記錄舊holder
這一步,所以呼叫的時候沒有完整的動畫