簡介
- 在 RecyclerView 中存在一個叫 pre_layout 的階段,與之對應的還有 real_layout、post_layout 的階段。
- 其中 pre_layout 階段的作用是記錄資料集改變前的子控件資訊(即動畫開始前的控件位置資訊),real_layout 是進行控件重新布局,post_layout階段的作用是記錄 real_layout 後的子控件位置資訊及觸發動畫。
分析
void dispatchLayout() {
...
if (mState.mLayoutStep == State.STEP_START) {
dispatchLayoutStep1();
...
dispatchLayoutStep2();
}
dispatchLayoutStep3();
...
}
複制
方法 dispatchLayout() 會在 RecyclerView.onLayout() 中被調用,其中dispatchLayoutStep1() 就是 pre_layout,dispatchLayoutStep3() 就是post_layout,而 dispatchLayoutStep2() 自然就是處理真正測量和布局子視圖(real_layout)。
分析
首先來看看 pre_layout 時都記錄了什麼内容:
private void dispatchLayoutStep1()
{
...
if(mState.mRunSimpleAnimations)
{
// Step 0: Find out where all non-removed items are, pre-layout
int count = mChildHelper.getChildCount();
for(int i = 0; i < count; ++i)
{
final ViewHolder holder = ...
...
final ItemHolderInfo animationInfo = mItemAnimator.recordPreLayoutInformation(...);
mViewInfoStore.addToPreLayout(holder, animationInfo);
...
}
}
...
}
複制
ItemHolderInfo 中封閉了對應 ViewHolder 的邊界資訊,即 ViewHolder 的 left、top、right、bottom值,對象 mViewInfoStore 的作用正如源碼注釋:
/**
* Keeps data about views to be used for animations
*/
final ViewInfoStore mViewInfoStore = new ViewInfoStore();
複制
再來看看 addToPreLayout() 方法:
void addToPreLayout(ViewHolder holder, ItemHolderInfo info)
{
InfoRecord record = mLayoutHolderMap.get(holder);
if(record == null)
{
record = InfoRecord.obtain();
mLayoutHolderMap.put(holder, record);
}
record.preInfo = info;
record.flags |= FLAG_PRE;
}
複制
由上可已看出 RecyclerView 将 pre-layout 階段的 ViewHolder 資訊存放在 ViewInfoStore.mLayoutHolderMap 集合中。
分析
接下來我們看看post_layout階段:
private void dispatchLayoutStep3()
{
...
if(mState.mRunSimpleAnimations)
{
// Step 3: Find out where things are now, and process change animations.
...
for(int i = mChildHelper.getChildCount() - 1; i >= 0; i--)
{...
final ItemHolderInfo animationInfo = mItemAnimator.recordPostLayoutInformation(mState, holder);
...
if(...)
{
...
animateChange(oldChangeViewHolder, holder, preInfo, postInfo, oldDisappearing, newDisappearing);
}
else
{
mViewInfoStore.addToPostLayout(holder, animationInfo);
}
}
// Step 4: Process view info lists and trigger animations
mViewInfoStore.process(mViewInfoProcessCallback);
}
...
}
複制
這是 addToPostLayout() 方法:
void addToPostLayout(ViewHolder holder, ItemHolderInfo info)
{
InfoRecord record = mLayoutHolderMap.get(holder);
if(record == null)
{
record = InfoRecord.obtain();
mLayoutHolderMap.put(holder, record);
}
record.postInfo = info;
record.flags |= FLAG_POST;
}
複制
與 pre-layout 階段相同,RecyclerView 也是将 post-layout 階段的 ViewHolder 資訊存放在 mViewInfoStore 的 mLayoutHolderMap 集合中,并且不難看出,同一個 ViewHolder 的 pre-layout 資訊與 post-layout 資訊封裝在了同一個 InfoRecord 對象中,分别叫InfoRecord.preInfo 與 InforRecord.postInfo,這樣 InfoRecord 就儲存着同一個 ViewHolder 在資料集變化前後的資訊,我們可以根據此資訊定義動畫的開始和結束狀态。