跟大家说明下,我分析的源码是LinearLayoutManager 里面的fill方法,Recycler系列都是采用这个布局里面的方法。
首先我提出了fill方法的所有源码。
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
RecyclerView.State state, boolean stopOnFocusable) {
// max offset we should set is mFastScroll + available
final int start = layoutState.mAvailable;
if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
// TODO ugly bug fix. should not happen
if (layoutState.mAvailable < 0) {
layoutState.mScrollingOffset += layoutState.mAvailable;
}
recycleByLayoutState(recycler, layoutState);
}
int remainingSpace = layoutState.mAvailable + layoutState.mExtra;
LayoutChunkResult layoutChunkResult = mLayoutChunkResult;
while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
layoutChunkResult.resetInternal();
layoutChunk(recycler, state, layoutState, layoutChunkResult);
if (layoutChunkResult.mFinished) {
break;
}
layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection;
/**
* Consume the available space if:
* * layoutChunk did not request to be ignored
* * OR we are laying out scrap children
* * OR we are not doing pre-layout
*/
if (!layoutChunkResult.mIgnoreConsumed || mLayoutState.mScrapList != null
|| !state.isPreLayout()) {
layoutState.mAvailable -= layoutChunkResult.mConsumed;
// we keep a separate remaining space because mAvailable is important for recycling
remainingSpace -= layoutChunkResult.mConsumed;
}
if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
layoutState.mScrollingOffset += layoutChunkResult.mConsumed;
if (layoutState.mAvailable < 0) {
layoutState.mScrollingOffset += layoutState.mAvailable;
}
recycleByLayoutState(recycler, layoutState);
}
if (stopOnFocusable && layoutChunkResult.mFocusable) {
break;
}
}
if (DEBUG) {
validateChildOrder();
}
return start - layoutState.mAvailable;
}
*************************************************************************************************************************************************首先看下官方的源码注解吧,非常重要。对把握全局很关键。并且貌似谷歌的源码写的还是非常切中要点的。
/**
* The magic functions :). Fills the given layout, defined by the layoutState. This is fairly
* independent from the rest of the {@link com.android.internal.widget.LinearLayoutManager}
* and with little change, can be made publicly available as a helper class.
填充由layoutState 定义的布局。此方法在LinearLayoutManager相当独立,跟其中别的方法关联性不强,可以单独出来形成帮助类。接下来的是传参。
* @param recycler Current recycler that is attached to RecyclerView
* @param layoutState Configuration on how we should fill out the available space.
* @param state Context passed by the RecyclerView to control scroll steps.
* @param stopOnFocusable If true, filling stops in the first focusable new child
//布局是否直接布局到第一个有焦点的子布局为止。
* @return Number of pixels that it added. Useful for scroll functions.
*/
// max offset we should set is mFastScroll + available
final int start = layoutState.mAvailable;
看下这个 int mAvailable,代表我们需要布局的空间。如果是纵向的那么就是纵向空间。但是如果存在滑动的情况,很显然,这个最大空间还应该加上这个滑动量,mFastScroll。
if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
// TODO ugly bug fix. should not happen
if (layoutState.mAvailable < 0) {
layoutState.mScrollingOffset += layoutState.mAvailable;
}
recycleByLayoutState(recycler, layoutState);
}
如果存在滑动距离,那么调用recycleByLayoutState 方法,现在看这个方法。
private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) {
if (!layoutState.mRecycle || layoutState.mInfinite) {
return;
}
if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) {
recycleViewsFromEnd(recycler, layoutState.mScrollingOffset);
} else {
recycleViewsFromStart(recycler, layoutState.mScrollingOffset);
}
}
//看方法是设置recycler的。找到一个recycleViewsFromEnd分析一下。
//官方解释是:回收那些由于滑动移除可视范围内的View。
private void recycleViewsFromEnd(RecyclerView.Recycler recycler, int dt) {
final int childCount = getChildCount();
if (dt < 0) {
if (DEBUG) {
Log.d(TAG, "Called recycle from end with a negative value. This might happen"
+ " during layout changes but may be sign of a bug");
}
return;
}
//最末位置的距离减去滑动的距离。
final int limit = mOrientationHelper.getEnd() - dt;
//是否是反向布局。目前我们考虑的都是正常的从上往下布局。
if (mShouldReverseLayout) {
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
//判断是否被移除了可视范围,这个时候需要回收此postion的ViewHolder。
if (mOrientationHelper.getDecoratedStart(child) < limit
|| mOrientationHelper.getTransformedStartWithDecoration(child) < limit) {
// stop here
recycleChildren(recycler, 0, i);
return;
}
}
} else {
for (int i = childCount - 1; i >= 0; i--) {
View child = getChildAt(i);
if (mOrientationHelper.getDecoratedStart(child) < limit
|| mOrientationHelper.getTransformedStartWithDecoration(child) < limit) {
// stop here
recycleChildren(recycler, childCount - 1, i);
return;
}
}
}
}
好了。 recycleByLayoutState(recycler, layoutState);分析完毕,主要考虑的是由于滑动造成的回收问题。所以看到RecyclerView的复杂性由于这个缓存机制已经变得复杂了。
int remainingSpace = layoutState.mAvailable + layoutState.mExtra;
//计算出还剩多少空间可以布局。
现在进入fill方法的核心逻辑。核心逻辑其实就是一个一个View去填充剩余空间。
while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state))
判断条件是个并,其一是mInfinite 不限制item的数量,或者剩余空间大于0。
hasMore方法是关联adapter,判断adapter是否还有data没有layout。
//重置ChunResult变量。
ayoutChunkResult.resetInternal();
//此方法已经在第一篇已经分析了。非常关键的方法,可以再退回去看下。
layoutChunk(recycler, state, layoutState, layoutChunkResult);
//是否可结束了。
if (layoutChunkResult.mFinished) {
break;
}
//计算已经布局的偏移。
layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection;
/**
* Consume the available space if:
* * layoutChunk did not request to be ignored
* * OR we are laying out scrap children
* * OR we are not doing pre-layout
*/
//减去可以利用的空间。这个条件中第一个是否忽略掉消费,第二个mScrapList 是否为空。第三个
是否处于layout前状态。
if (!layoutChunkResult.mIgnoreConsumed || mLayoutState.mScrapList != null
|| !state.isPreLayout()) {
layoutState.mAvailable -= layoutChunkResult.mConsumed;
// we keep a separate remaining space because mAvailable is important for recycling
remainingSpace -= layoutChunkResult.mConsumed;
}
//计算此mScrollingOffset 的值,这个代表滚动值,当然布局一个view,滚动值也增加了。如果可以利用空间小于0,那么还得减去这个量。这个值代表你累计同一个滚动了多少距离。它主要的逻辑是会一直累计布局的View的消费量。
if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {
layoutState.mScrollingOffset += layoutChunkResult.mConsumed;
if (layoutState.mAvailable < 0) {
layoutState.mScrollingOffset += layoutState.mAvailable;
}
//由于布局一个可能会引起一些进入不可视的状态,走一遍回收过程。
recycleByLayoutState(recycler, layoutState);
}
//判断是否需要停止布局。
if (stopOnFocusable && layoutChunkResult.mFocusable) {
break;
}
至此fill方法方法分析完毕。如果还是有点云里雾里这个很正常,等整个Recycler系列都结束了,再来串其中几个变量的值意义。