上一篇:RecyclerView与ListView对比浅析(二):View缓存篇
(三)绘制篇
1. AbsListView(源码版本 4.4)
绘制就分三步来分析Measure、Layout和Draw
(1)onMeasure里未用到MeasureSpec的高宽,首先设Selector,然后根据SelectionPadding和mPadding算出ListPadding的上下左右。最后根据TranscriptMode,决定Scroll和Measure的先后顺序。@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (mSelector == null) { useDefaultSelector(); } final Rect listPadding = mListPadding; listPadding.left = mSelectionLeftPadding + mPaddingLeft; listPadding.top = mSelectionTopPadding + mPaddingTop; listPadding.right = mSelectionRightPadding + mPaddingRight; listPadding.bottom = mSelectionBottomPadding + mPaddingBottom; // Check if our previous measured size was at a point where we should scroll later. if (mTranscriptMode == TRANSCRIPT_MODE_NORMAL) { final int childCount = getChildCount(); final int listBottom = getHeight() - getPaddingBottom(); final View lastChild = getChildAt(childCount - 1); final int lastBottom = lastChild != null ? lastChild.getBottom() : listBottom; mForceTranscriptScroll = mFirstPosition + childCount >= mLastHandledItemCount && lastBottom <= listBottom; } }
(2)onLayout中,首先调super.onLayout,然后:
a. 如果changed为true,遍历拿到所有Child执行forceLayout():清空MeasureCache,设mPrivateFlags为FORCE_LAYOUT,然后调mRecycler的markChildrenDirty():对所有缓存的View执行forceLayout()
b. 如果mFastScroller不为空且ItemCount变了,调mFastScroller的onItemCountChanged。
c. 调layoutChildren(),需由子类实现。
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); mInLayout = true; if (changed) { int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { getChildAt(i).forceLayout(); } mRecycler.markChildrenDirty(); } if (mFastScroller != null && (mItemCount != mOldItemCount || mDataChanged)) { mFastScroller.onItemCountChanged(mItemCount); } layoutChildren(); mInLayout = false; mOverscrollMax = (b - t) / OVERSCROLL_LIMIT_DIVISOR; }
(3)draw调完super.draw后,处理了mEdgeGlowTop和mEdgeGlowBottom
2. ListView(源码版本 4.4)
同样分三步Measure、Layout和Draw继续分析:
(1)onMeasure中,先调父类的onMeasure,然后调父类的obtainView拿到 位置为0的View(mIsScrap作为入参记录是否为ScrapView),调measureScrapChild对它Measure,拿到childWidth、childHeight和childState,然后判断这个Type的View是否需要回收,需要就调addScrapView(child, -1)。最后根据childWidth和childHeight算出widthSize和heightSize(不同Mode用不同的计算方法),调setMeasuredDivision。@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 设置mListPadding super.onMeasure(widthMeasureSpec, heightMeasureSpec); int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int childWidth = 0; int childHeight = 0; int childState = 0; mItemCount = mAdapter == null ? 0 : mAdapter.getCount(); if (mItemCount > 0 && (widthMode == MeasureSpec.UNSPECIFIED || heightMode == MeasureSpec.UNSPECIFIED)) { final View child = obtainView(0, mIsScrap); measureScrapChild(child, 0, widthMeasureSpec); childWidth = child.getMeasuredWidth(); childHeight = child.getMeasuredHeight(); childState = combineMeasuredStates(childState, child.getMeasuredState()); if (recycleOnMeasure() && mRecycler.shouldRecycleViewType( ((LayoutParams) child.getLayoutParams()).viewType)) { mRecycler.addScrapView(child, -1); } } 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) { // TODO: 这里可以用首个可见View的位置,而不是0 heightSize = measureHeightOfChildren(widthMeasureSpec, 0, NO_POSITION, heightSize, -1); } setMeasuredDimension(widthSize , heightSize); mWidthMeasureSpec = widthMeasureSpec; }
(2)ListView没有重写onLayout,而是沿用父类的onLayout并实现了layoutChildren(),来看下layoutChildren的过程(源码见上一节);
a. 如果Adapter为空,则重置List,移除Layout中的所有View。
b. 根据LayoutMode选择不同Layout方式,这里只看default的方式:用getChildAt方法拿到几个View备用:原来选择的oldSel、原来在首位的oldFirst和新选择的newSel。
c. 如果dataChanged为true,调handleDataChanged()处理选择和同步的相应变化。
d. 所有ChildView放入RecycleBin,数据变了就放ScrapViews,没变就放ActiveViews,然后detach所有旧View。
e. 根据LayoutMode,进行View填充,这里也是只看default方法:如果childCount==0,就根据List是自顶向下还是自底向上选择fill方法,正常是选择fillFromTop(childrenTop)进行填充;如果不为0,则根据先前计算的SelectionPosition调fillSpecific填充特定的View。
f. fill完毕后,把所有ActiveViews存入ScrapView
在这里重点分析下fillFromTop的源码:
// 从上向下在ListView中填充Item View private View fillFromTop(int nextTop) { mFirstPosition = Math.min(mFirstPosition, mSelectedPosition); mFirstPosition = Math.min(mFirstPosition, mItemCount - 1); if (mFirstPosition < 0) { mFirstPosition = 0; } // 具体操作在此 return fillDown(mFirstPosition, nextTop); }
// 在位置pos下面填充Item View private View fillDown(int pos, int nextTop) { View selectedView = null; // ListView getHeight也是这样计算的 int end = (mBottom - mTop); …… // 初始化时pos = 0,如果item总数少于一屏幕,执行mItemCount - pos次。如果item多于一屏幕,执行end - nextTop次。 while (nextTop < end && pos < mItemCount) { // is this the selected item? boolean selected = pos == mSelectedPosition; // 获取Item View对象,在View缓存中已经介绍过了,如果数据没变,从ActiveViews里拿,如果变了,调obtainView查找缓存拿View 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; }
在makeAndAddView中,拿到View后,还会调setupChild进行状态设置
private void setupChild(View child, int position, int y, boolean flowDown, int childrenLeft, boolean selected, boolean recycled) { // 判断当前Item View是否选中状态 final boolean isSelected = selected && shouldShowSelector(); final boolean updateChildSelected = isSelected != child.isSelected(); final int mode = mTouchMode; // 是否处于按下状态 final boolean isPressed = mode > TOUCH_MODE_DOWN && mode < TOUCH_MODE_SCROLL && mMotionPosition == position; final boolean updateChildPressed = isPressed != child.isPressed(); // 是否需要重新measure与layout final boolean needToMeasure = !recycled || updateChildSelected || child.isLayoutRequested(); // Respect layout params that are already in the view. Otherwise make some up... // noinspection unchecked AbsListView.LayoutParams p = (AbsListView.LayoutParams) child.getLayoutParams(); if (p == null) { p = (AbsListView.LayoutParams) generateDefaultLayoutParams(); } p.viewType = mAdapter.getItemViewType(position); if ((recycled && !p.forceAdd) || (p.recycledHeaderFooter && p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER)) { attachViewToParent(child, flowDown ? -1 : 0, p); } else { p.forceAdd = false; if (p.viewType == AdapterView.ITEM_VIEW_TYPE_HEADER_OR_FOOTER) { p.recycledHeaderFooter = true; } // 向ListView(ViewGroup子类)添加当前Item View addViewInLayout(child, flowDown ? -1 : 0, p, true); } // 更新选中状态 if (updateChildSelected) { child.setSelected(isSelected); } // 更新按下状态 if (updateChildPressed) { child.setPressed(isPressed); } if (mChoiceMode != CHOICE_MODE_NONE && mCheckStates != null) { if (child instanceof Checkable) { ((Checkable) child).setChecked(mCheckStates.get(position)); } else if (getContext().getApplicationInfo().targetSdkVersion >= android.os.Build.VERSION_CODES.HONEYCOMB) { child.setActivated(mCheckStates.get(position)); } } if (needToMeasure) { int childWidthSpec = ViewGroup.getChildMeasureSpec(mWidthMeasureSpec, mListPadding.left + mListPadding.right, p.width); int lpHeight = p.height; int childHeightSpec; if (lpHeight > 0) { childHeightSpec = MeasureSpec.makeMeasureSpec(lpHeight, MeasureSpec.EXACTLY); } else { childHeightSpec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED); } // 与普通视图的measure流程不同,ListView是在此处执行具体的当前Item View measure child.measure(childWidthSpec, childHeightSpec); } else { cleanupLayoutState(child); } final int w = child.getMeasuredWidth(); final int h = child.getMeasuredHeight(); final int childTop = flowDown ? y : y - h; if (needToMeasure) { final int childRight = childrenLeft + w; final int childBottom = childTop + h; // 大小改变肯定位置也会发生变化,当前Item View重新进行layout child.layout(childrenLeft, childTop, childRight, childBottom); } else { child.offsetLeftAndRight(childrenLeft - child.getLeft()); child.offsetTopAndBottom(childTop - child.getTop()); } if (mCachingStarted && !child.isDrawingCacheEnabled()) { child.setDrawingCacheEnabled(true); } if (recycled && (((AbsListView.LayoutParams)child.getLayoutParams()).scrappedFromPosition) != position) { child.jumpDrawablesToCurrentState(); } }
3. RecyclerView(源码版本 5.1.1)
同样分三步分析,Measure、Layout和Draw
(1)onMeasure
@Override protected void onMeasure(int widthSpec, int heightSpec) { //Adapter在Measure过程中执行了更新 if (mAdapterUpdateDuringMeasure) { //中止Layout请求 eatRequestLayout(); //执行更新并且设置动画 processAdapterUpdatesAndSetAnimationFlags(); if (mState.mRunPredictiveAnimations) { //处理Predictive动画的机制 // TODO: try to provide a better approach. // When RV decides to run predictive animations, we need to measure in pre-layout // state so that pre-layout pass results in correct layout. // On the other hand, this will prevent the layout manager from resizing properly. mState.mInPreLayout = true; } else { // consume remaining updates to provide a consistent state with the layout pass. mAdapterHelper.consumeUpdatesInOnePass(); mState.mInPreLayout = false; } mAdapterUpdateDuringMeasure = false; //恢复Layout请求 resumeRequestLayout(false); } if (mAdapter != null) { mState.mItemCount = mAdapter.getItemCount(); } else { mState.mItemCount = 0; } if (mLayout == null) { //默认Measure方法 defaultOnMeasure(widthSpec, heightSpec); } else { //调LayoutManager的onMeasure mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec); } mState.mInPreLayout = false; // clear }
首先看defaultOnMeasure,与普通View的Measure基本相似,都是根据Mode得出对应的高宽,最后setMeasuredDimision。而LayoutManager的onMeasure就是调mRecyclerView.defaultOnMeasure,就不贴代码了。
private void defaultOnMeasure(int widthSpec, int heightSpec) { final int widthMode = MeasureSpec.getMode(widthSpec); final int heightMode = MeasureSpec.getMode(heightSpec); final int widthSize = MeasureSpec.getSize(widthSpec); final int heightSize = MeasureSpec.getSize(heightSpec); int width = 0; int height = 0; switch (widthMode) { case MeasureSpec.EXACTLY: case MeasureSpec.AT_MOST: width = widthSize; break; case MeasureSpec.UNSPECIFIED: default: width = ViewCompat.getMinimumWidth(this); break; } switch (heightMode) { case MeasureSpec.EXACTLY: case MeasureSpec.AT_MOST: height = heightSize; break; case MeasureSpec.UNSPECIFIED: default: height = ViewCompat.getMinimumHeight(this); break; } setMeasuredDimension(width, height); }
(2)onLayout
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { //中止Layout请求 eatRequestLayout(); //分发Layout任务 dispatchLayout(); //恢复Layout请求 resumeRequestLayout(false); mFirstLayoutComplete = true; }
DispatchLayout()是其中的关键函数,首先看下函数描述:在layoutChildren()外面包一层,用于处理layout引发的动画变化。这里的layoutChildren()就是表示前面介绍的LayoutManager中的onLayoutChildren(),处理各个Child的Measure操作。源码很长,就分步看下吧:
void dispatchLayout() { if (mAdapter == null) { Log.e(TAG, "No adapter attached; skipping layout"); return; } if (mLayout == null) { Log.e(TAG, "No layout manager attached; skipping layout"); return; } mDisappearingViewsInLayoutPass.clear(); //这里再调一次中止Layout请求 eatRequestLayout(); mRunningLayoutOrScroll = true; //处理Adapter更新并设动画标志位 processAdapterUpdatesAndSetAnimationFlags(); //一些参数的初始化 mState.mOldChangedHolders = mState.mRunSimpleAnimations && mItemsChanged && supportsChangeAnimations() ? new ArrayMap<Long, ViewHolder>() : null; mItemsAddedOrRemoved = mItemsChanged = false; ArrayMap<View, Rect> appearingViewInitialBounds = null; mState.mInPreLayout = mState.mRunPredictiveAnimations; mState.mItemCount = mAdapter.getItemCount(); findMinMaxChildLayoutPositions(mMinMaxLayoutPositions); if (mState.mRunSimpleAnimations) { // 准备工作:找到所有免移除的item,执行预layout mState.mPreLayoutHolderMap.clear(); mState.mPostLayoutHolderMap.clear(); int count = mChildHelper.getChildCount(); for (int i = 0; i < count; ++i) { final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i)); //判断条件:是否忽视、是否合法、Adapter是否有StableId if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) { continue; } final View view = holder.itemView; mState.mPreLayoutHolderMap.put(holder, new ItemHolderInfo(holder, view.getLeft(), view.getTop(), view.getRight(), view.getBottom())); } } if (mState.mRunPredictiveAnimations) { // 第一步:执行预Layout,需要用到item的原位置。LayoutManager会对所有元素进行layout,包括已移除item(但不会把已移除项再加回来)。这就能让pre-layout拿到用户可见View的position,真正执行layout时不会变。 //存储原位置方便LayoutManager执行映射逻辑 saveOldPositions(); //前面的processAdapterUpdatesAndSetAnimationFlags已经对动画执行了预Layiout //遍历mChildHelper,将里面已改变、未移除且不该忽略的holder移出预Layout容器 if (mState.mOldChangedHolders != null) { int count = mChildHelper.getChildCount(); for (int i = 0; i < count; ++i) { final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i)); if (holder.isChanged() && !holder.isRemoved() && !holder.shouldIgnore()) { long key = getChangedHolderKey(holder); mState.mOldChangedHolders.put(key, holder); mState.mPreLayoutHolderMap.remove(holder); } } } final boolean didStructureChange = mState.mStructureChanged; //因为是预Layout,暂时把此标志位置false mState.mStructureChanged = false; //执行onLayoutChildren,不同的LayoutManager子类有不同的实现 mLayout.onLayoutChildren(mRecycler, mState); mState.mStructureChanged = didStructureChange; //设置即将显示View的对应Bounds appearingViewInitialBounds = new ArrayMap<View, Rect>(); for (int i = 0; i < mChildHelper.getChildCount(); ++i) { boolean found = false; View child = mChildHelper.getChildAt(i); if (getChildViewHolderInt(child).shouldIgnore()) { continue; } for (int j = 0; j < mState.mPreLayoutHolderMap.size(); ++j) { ViewHolder holder = mState.mPreLayoutHolderMap.keyAt(j); if (holder.itemView == child) { found = true; break; } } if (!found) { appearingViewInitialBounds.put(child, new Rect(child.getLeft(), child.getTop(), child.getRight(), child.getBottom())); } } // 不处理disappearingList是因为它会在Post Layout的时候处理 clearOldPositions(); mAdapterHelper.consumePostponedUpdates(); } else { //执行完预Layout但决定不执行Predictive动画的情况 clearOldPositions(); mAdapterHelper.consumeUpdatesInOnePass(); //遍历mChildHelper,对mState里的两容器的元素进行更新 if (mState.mOldChangedHolders != null) { int count = mChildHelper.getChildCount(); for (int i = 0; i < count; ++i) { final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i)); if (holder.isChanged() && !holder.isRemoved() && !holder.shouldIgnore()) { long key = getChangedHolderKey(holder); mState.mOldChangedHolders.put(key, holder); mState.mPreLayoutHolderMap.remove(holder); } } } } mState.mItemCount = mAdapter.getItemCount(); mState.mDeletedInvisibleItemCountSincePreviousLayout = 0; //第二步:执行Layout mState.mInPreLayout = false; mLayout.onLayoutChildren(mRecycler, mState); mState.mStructureChanged = false; mPendingSavedState = null; // onLayoutChildren有可能导致禁用item动画,这里重新检查一下 mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null; if (mState.mRunSimpleAnimations) { // 第三步:执行后Layout ArrayMap<Long, ViewHolder> newChangedHolders = mState.mOldChangedHolders != null ? new ArrayMap<Long, ViewHolder>() : null; int count = mChildHelper.getChildCount(); for (int i = 0; i < count; ++i) { ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i)); if (holder.shouldIgnore()) { continue; } final View view = holder.itemView; long key = getChangedHolderKey(holder); if (newChangedHolders != null && mState.mOldChangedHolders.get(key) != null) { newChangedHolders.put(key, holder); } else { //需要后layout处理的Holder存入这个容器 mState.mPostLayoutHolderMap.put(holder, new ItemHolderInfo(holder, view.getLeft(), view.getTop(), view.getRight(), view.getBottom())); } } processDisappearingList(appearingViewInitialBounds); //第四步:执行移除和消失动画 int preLayoutCount = mState.mPreLayoutHolderMap.size(); for (int i = preLayoutCount - 1; i >= 0; i--) { ViewHolder itemHolder = mState.mPreLayoutHolderMap.keyAt(i); if (!mState.mPostLayoutHolderMap.containsKey(itemHolder)) { ItemHolderInfo disappearingItem = mState.mPreLayoutHolderMap.valueAt(i); mState.mPreLayoutHolderMap.removeAt(i); View disappearingItemView = disappearingItem.holder.itemView; mRecycler.unscrapView(disappearingItem.holder); animateDisappearance(disappearingItem); } } // 第五步:执行新增动画 int postLayoutCount = mState.mPostLayoutHolderMap.size(); if (postLayoutCount > 0) { for (int i = postLayoutCount - 1; i >= 0; i--) { ViewHolder itemHolder = mState.mPostLayoutHolderMap.keyAt(i); ItemHolderInfo info = mState.mPostLayoutHolderMap.valueAt(i); if ((mState.mPreLayoutHolderMap.isEmpty() || !mState.mPreLayoutHolderMap.containsKey(itemHolder))) { mState.mPostLayoutHolderMap.removeAt(i); Rect initialBounds = (appearingViewInitialBounds != null) ? appearingViewInitialBounds.get(itemHolder.itemView) : null; animateAppearance(itemHolder, initialBounds, info.left, info.top); } } } //第6步,执行永久项的动画 count = mState.mPostLayoutHolderMap.size(); for (int i = 0; i < count; ++i) { ViewHolder postHolder = mState.mPostLayoutHolderMap.keyAt(i); ItemHolderInfo postInfo = mState.mPostLayoutHolderMap.valueAt(i); ItemHolderInfo preInfo = mState.mPreLayoutHolderMap.get(postHolder); if (preInfo != null && postInfo != null) { if (preInfo.left != postInfo.left || preInfo.top != postInfo.top) { postHolder.setIsRecyclable(false); if (DEBUG) { Log.d(TAG, "PERSISTENT: " + postHolder + " with view " + postHolder.itemView); } if (mItemAnimator.animateMove(postHolder, preInfo.left, preInfo.top, postInfo.left, postInfo.top)) { postAnimationRunner(); } } } } // 第7步:执行改变项的动画 count = mState.mOldChangedHolders != null ? mState.mOldChangedHolders.size() : 0; // 逆向遍历以免View在遍历过程被回收 for (int i = count - 1; i >= 0; i--) { long key = mState.mOldChangedHolders.keyAt(i); ViewHolder oldHolder = mState.mOldChangedHolders.get(key); View oldView = oldHolder.itemView; if (oldHolder.shouldIgnore()) { continue; } if (mRecycler.mChangedScrap != null && mRecycler.mChangedScrap.contains(oldHolder)) { animateChange(oldHolder, newChangedHolders.get(key)); } else if (DEBUG) { Log.e(TAG, "cannot find old changed holder in changed scrap :/" + oldHolder); } } } resumeRequestLayout(false); mLayout.removeAndRecycleScrapInt(mRecycler); mState.mPreviousLayoutItemCount = mState.mItemCount; mDataSetHasChangedAfterLayout = false; mState.mRunSimpleAnimations = false; mState.mRunPredictiveAnimations = false; mRunningLayoutOrScroll = false; mLayout.mRequestedSimpleAnimations = false; if (mRecycler.mChangedScrap != null) { mRecycler.mChangedScrap.clear(); } mState.mOldChangedHolders = null; if (didChildRangeChange(mMinMaxLayoutPositions[0], mMinMaxLayoutPositions[1])) { notifyOnScrolled(0, 0); } }
由以上代码可知,onLayout分为预Layout、正式Layout和后Layout三步,这一操作主要是为了实现item的出现、消失等效果。
(3)onDraw()
onDraw主要是调用的父类的onDraw,后面加入了itemDecorations的draw操作,它常用来画分隔线Divider。
@Override public void onDraw(Canvas c) { super.onDraw(c); final int count = mItemDecorations.size(); for (int i = 0; i < count; i++) { mItemDecorations.get(i).onDraw(c, this, mState); } }