ListView 的视图循环使用机制主要实现自内部的 RecyclerBin。
通过 ListView 的继承关系,可以看出它继承自 AbsListView,是个 ViewGroup。ViewGroup 的绘制流程无非就是 measure -> layout -> draw。
ListView 中实现了自己的 onMeasure
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 if (widthMode == MeasureSpec.UNSPECIFIED) { widthSize = mListPadding.left + mListPadding.right + childWidth + getVerticalScrollbarWidth(); } else { widthSize |= (childState & MEASURED_STATE_MASK); } if (heightMode == MeasureSpec.UNSPECIFIED) { heightSize = mListPadding.top + mListPadding.bottom + childHeight + getVerticalFadingEdgeLength() * 2 ; } if (heightMode == MeasureSpec.AT_MOST) { heightSize = measureHeightOfChildren(widthMeasureSpec, 0 , NO_POSITION, heightSize, -1 ); }
没什么特别的,把各种尺寸和子View的measure求和。然后在 ListView 中没有发现 onLayout,进入 AbsListView 查看。
1 2 3 4 5 6 7 8 9 protected void onLayout (boolean changed, int l, int t, int r, int b) { super .onLayout(changed, l, t, r, b); mInLayout = true ; ...... layoutChildren(); ...... mInLayout = false ; }
会调用 layoutChildren,ListView 中实现了这个方法。直接到核心步骤。
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 ...... final int firstPosition = mFirstPosition;final RecycleBin recycleBin = mRecycler;if (dataChanged) { for (int i = 0 ; i < childCount; i++) { recycleBin.addScrapView(getChildAt(i), firstPosition+i); } } else { recycleBin.fillActiveViews(childCount, firstPosition); } detachAllViewsFromParent(); recycleBin.removeSkippedScrap(); switch (mLayoutMode) {...... default : if (childCount == 0 ) { if (!mStackFromBottom) { final int position = lookForSelectablePosition(0 , true ); setSelectedPositionInt(position); sel = fillFromTop(childrenTop); } else { final int position = lookForSelectablePosition(mItemCount - 1 , false ); setSelectedPositionInt(position); sel = fillUp(mItemCount - 1 , childrenBottom); } } else { if (mSelectedPosition >= 0 && mSelectedPosition < mItemCount) { sel = fillSpecific(mSelectedPosition, oldSel == null ? childrenTop : oldSel.getTop()); } else if (mFirstPosition < mItemCount) { sel = fillSpecific(mFirstPosition, oldFirst == null ? childrenTop : oldFirst.getTop()); } else { sel = fillSpecific(0 , childrenTop); } } break ; } recycleBin.scrapActiveViews();
childCount
mStackFromBottom: AbsListView 中的属性,默认为 false,表示列表将会从上到下的填充,反之从下到上填充
不单独调用 setStackFromBottom(true)的话, ListView 就会从上到下开始填充,调用 fillFromTop
,接着调用 fillDown
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 private View fillDown (int pos, int nextTop) { View selectedView = null ; int end = (mBottom - mTop); if ((mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK) { end -= mListPadding.bottom; } while (nextTop < end && pos < mItemCount) { boolean selected = pos == mSelectedPosition; View child = makeAndAddView(pos, nextTop, true , mListPadding.left, selected); nextTop = child.getBottom() + mDividerHeight; if (selected) { selectedView = child; } pos++; } setVisibleRangeHint(mFirstPosition, mFirstPosition + getChildCount() - 1 ); return selectedView; }
fillDown
里循环进行了 子View 的填充。
end: ListView 的高度
nextTop: 表示单个 子View的顶部 相对于 ListView 的位置高度
pos: 进入 ListView 的第一个位置,默认为 0,没填充一个子 View 就 +1
循环条件 nextTop < end && pos < mItemCount
:ListView 的高度要大于下一个子 View 的顶部位置 && pos 要小于 adapter 里返回的 ItemCount。这样就确保 ListView 只会填充一定数量,它自身高度能够显示完全的子 View。
进入 makeAndAddView
方法中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 private View makeAndAddView (int position, int y, boolean flow, int childrenLeft, boolean selected) { if (!mDataChanged) { final View activeView = mRecycler.getActiveView(position); if (activeView != null ) { setupChild(activeView, position, y, flow, childrenLeft, selected, true ); return activeView; } } final View child = obtainView(position, mIsScrap); setupChild(child, position, y, flow, childrenLeft, selected, mIsScrap[0 ]); return child; }
如果数据没有改变,会先从 recycler 中获取与指定位置对应的视图,如果找到了就直接设置然后返回;没找到就会调用 AbsListView 的 obtainView
获取一个 View,然后设置。
未完待续