在上一篇文章中,我们主要分析了RecyclerView中的几个重要的内部类。今天我们就把它们串起来,看它们分别是如何工作的。

RecyclerView继承自ViewGroup。所以肯定是通过addView的方式来将所有的item添加进来的。所以在分析过程中,我先找到addView的调用位置,然后一步步倒推,看view是如何获取的。

首先,在创建ChildHelper时传入的Callback中调用了addView()方法。除了调用了addView()之外,还调用了removeViewAt等方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
private void initChildrenHelper() {
    mChildHelper = new ChildHelper(new ChildHelper.Callback() {
        @Override
        public int getChildCount() {
            return RecyclerView.this.getChildCount();
        }

        @Override
        public void addView(View child, int index) {
            if (VERBOSE_TRACING) {
                TraceCompat.beginSection("RV addView");
            }
            //调用ViewGroup的addView
            RecyclerView.this.addView(child, index);
            //...
        }

        @Override
        public int indexOfChild(View view) {
            return RecyclerView.this.indexOfChild(view);
        }

        @Override
        public void removeViewAt(int index) {
            final View child = RecyclerView.this.getChildAt(index);
            //...
            RecyclerView.this.removeViewAt(index);
            //...
        }

        @Override
        public View getChildAt(int offset) {
            return RecyclerView.this.getChildAt(offset);
        }

        @Override
        public void removeAllViews() {
            //...
            RecyclerView.this.removeAllViews();
        }

        @Override
        public ViewHolder getChildViewHolder(View view) {
            return getChildViewHolderInt(view);
        }

        @Override
        public void attachViewToParent(View child, int index,
                ViewGroup.LayoutParams layoutParams) {
            //...        
            RecyclerView.this.attachViewToParent(child, index, layoutParams);
        }

        @Override
        public void detachViewFromParent(int offset) {
            //...
            RecyclerView.this.detachViewFromParent(offset);
        }

        @Override
        public void onEnteredHiddenState(View child) {
            final ViewHolder vh = getChildViewHolderInt(child);
            if (vh != null) {
                vh.onEnteredHiddenState(RecyclerView.this);
            }
        }

        @Override
        public void onLeftHiddenState(View child) {
            final ViewHolder vh = getChildViewHolderInt(child);
            if (vh != null) {
                vh.onLeftHiddenState(RecyclerView.this);
            }
        }
    });
}

接下来,看何时执行Callback中的回调方法。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
//ChildHelper addView方法
void addView(View child, int index, boolean hidden) {
    final int offset;
    if (index < 0) {
        offset = mCallback.getChildCount();
    } else {
        offset = getOffset(index);
    }
    mBucket.insert(offset, hidden);
    if (hidden) {
        hideViewInternal(child);
    }
    //执行add方法
    mCallback.addView(child, offset);
}
//LayoutManager的addViewInt方法
private void addViewInt(View child, int index, boolean disappearing) {
    //....
    if (holder.wasReturnedFromScrap() || holder.isScrap()) {
        //...
    } else if (child.getParent() == mRecyclerView) { // it was not a scrap but a valid child
        // ensure in correct position
        //...
    } else {
        //调用addView方法
        mChildHelper.addView(child, index, false);
        lp.mInsetsDirty = true;
        if (mSmoothScroller != null && mSmoothScroller.isRunning()) {
            mSmoothScroller.onChildAttachedToWindow(child);
        }
    }
    if (lp.mPendingInvalidate) {
        holder.itemView.invalidate();
        lp.mPendingInvalidate = false;
    }
}
//LayoutManager的addView
public void addView(View child, int index) {
    addViewInt(child, index, false);
}

通过一路追踪,我们追踪到了LayoutManageraddView方法。接下来就是找,在哪里获取的view,然后调用的addView方法。这里我们以LinearLayoutManager为例。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
//LiearLayoutManager layoutChunk方法
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
        LayoutState layoutState, LayoutChunkResult result) {
    //获取view
    View view = layoutState.next(recycler);
    RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();
    if (layoutState.mScrapList == null) {
        if (mShouldReverseLayout == (layoutState.mLayoutDirection
                == LayoutState.LAYOUT_START)) {
            addView(view);
        } else {
            addView(view, 0);
        }
    } else {
        //...
    }
    //...
}

LinearLayoutManager调用LayoutStatenext方法获取viewLayoutState是一个工具类,用来保存LayoutManager填充时的临时状态。LayoutState方法调用了RecyclergetViewForPosition方法获取ViewHolder。 对于getViewForPosition之后的逻辑,涉及到RecyclerView的缓存,比较复杂,所以单独放到下一篇去讲。

1
2
3
4
5
6
7
8
9

View next(RecyclerView.Recycler recycler) {
    if (mScrapList != null) {
        return nextViewFromScrapList();
    }
    final View view = recycler.getViewForPosition(mCurrentPosition);
    mCurrentPosition += mItemDirection;
    return view;
}

那么又是谁调用了layoutChunk呢。我们继续追踪:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
//LinearLayoutManager fill方法
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
        RecyclerView.State state, boolean stopOnFocusable) {
    //        
    int remainingSpace = layoutState.mAvailable + layoutState.mExtraFillSpace;
    LayoutChunkResult layoutChunkResult = mLayoutChunkResult;
    //不断循环获得新的item用于填充,直到没有填充空间
    while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
        layoutChunkResult.resetInternal();
        //...
        layoutChunk(recycler, state, layoutState, layoutChunkResult);
        //...
    }
    return start - layoutState.mAvailable;
}

fill方法被LinearLayoutManageronLayoutChildren方法调用。由于调用的次数比较多,而且代码比较长,这里就不贴代码了。

onLayoutChildren方法被RecyclerViewdispatchLayoutStep1dispatchLayoutStep2调用,而这两个方法也在RecyclerViewonMeasureonLayout方法调用。至此,RecyclerView整个流程分析完成。最后我们用一张图来总结一下。