天天看點

[Article] RecyclerView:pre_layout:Nu2

簡介
  1. 在 RecyclerView 中存在一個叫 pre_layout 的階段,與之對應的還有 real_layout、post_layout 的階段。
  2. 其中 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 在資料集變化前後的資訊,我們可以根據此資訊定義動畫的開始和結束狀态。