RecyclerView分析
来源:互联网 发布:网络加密技术 编辑:程序博客网 时间:2024/06/15 18:51
概要
RecyclerView能够在有限的视图中展示大量的数据,RecyclerView只会和ViewHolder进行接触,而Adapter的工作就是将Data转换为RecyclerView认识的ViewHolder,因此RecyclerView就间接地认识了Data。而LayoutManager负责完成布局的具体工作,而Recycler负责对 View进行管理,而ItemAnimator负责与View相关的动画;
RecyclerView.onMeasure()
@Overrideprotected void onMeasure(int widthSpec, int heightSpec) { //mLayout指的是LayoutManager,如果为空,则走RecyclerView的Measure过程(defaultOnMeasure) if (mLayout == null) { defaultOnMeasure(widthSpec, heightSpec); return; } //对于LinearLayoutManager来说mAutoMeasure会被默认设置为ture; //而GridLayoutManager是继承自LinearLayoutManager,因此默认也是 ture; if (mLayout.mAutoMeasure) { final int widthMode = MeasureSpec.getMode(widthSpec); final int heightMode = MeasureSpec.getMode(heightSpec); final boolean skipMeasure = widthMode == MeasureSpec.EXACTLY && heightMode == MeasureSpec.EXACTLY; //这里最终还是走RecyclerView的Measure过程(defaultOnMeasure) mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec); //如果宽高都是EXACTLY(确定的)或者没设置 Adapter,则结束测量 if (skipMeasure || mAdapter == null) { return; } // RecyclerView.State ,这个类封装了当前RecyclerView的诸多信息,包括焦点,滚动,资源 id.....; //State的一个变量mLayoutStep表示了RecyclerView当前的布局状态,包括STEP_START、STEP_LAYOUT 、 STEP_ANIMATIONS三个; //RecyclerView的布局过程也分为三步,step1负责记录状态,step2负责布局,step3则与step1进行比较,根据变化来触发动画。 //第一步:设置一些 Viewr的基本信息,如果有动画遍历当前所有子 View,拿到 ViewHolder与ItemHolderInfo(animationInfo), //然后将ItemHolderInfo信息赋值给InfoRecord的preInfo变量。 //最后标记InfoRecord的flags为FLAG_PRE,并将ViewHolder、InfoRecord二者放入mViewInfoStore的mLayoutHolderMap中 //测量第一步,移步dispatchLayoutStep1()方法 if (mState.mLayoutStep == State.STEP_START) { dispatchLayoutStep1(); } mLayout.setMeasureSpecs(widthSpec, heightSpec); mState.mIsMeasuring = true; //测量第二步,移步dispatchLayoutStep2()方法 dispatchLayoutStep2(); } else { //********************************省略********************************* }}
RecyclerView.dispatchLayoutStep1()
private void dispatchLayoutStep1() { mState.assertLayoutStep(State.STEP_START); mState.mIsMeasuring = false; //通过加减计数的方式,判断是否需要忽略来自child 的 requestLayout的调用 eatRequestLayout(); //mViewInfoStore主要存储了些跟动画有关的信息 mViewInfoStore.clear(); //通过计数来标记当前是否处于 Layout或 Scroll 状态,处于这两个状态时,一些动作是不允许的(比如更新 adapter 这个动作) //这里是加 onEnterLayoutOrScroll(); //决定是否跑动画以及跑何种动画 processAdapterUpdatesAndSetAnimationFlags(); //找到当前焦点在那个Child View上 saveFocusInfo(); mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged; mItemsAddedOrRemoved = mItemsChanged = false; mState.mInPreLayout = mState.mRunPredictiveAnimations; mState.mItemCount = mAdapter.getItemCount(); //找到当前屏幕中完全显示的Child View 的最大最小位置,存入mMinMaxLayoutPositions中 //ps:以参数作为返回值也是醉了 findMinMaxChildLayoutPositions(mMinMaxLayoutPositions); if (mState.mRunSimpleAnimations) { //这里会遍历所有item,然后找到所有没有被移除的 item int count = mChildHelper.getChildCount(); for (int i = 0; i < count; ++i) { final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i)); if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) { continue; } //ItemHolderInfo记录着 item 的边界坐标信息(left、top、right、bottom)这些信息用来跑动画 final ItemHolderInfo animationInfo = mItemAnimator.recordPreLayoutInformation(mState, holder, ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),holder.getUnmodifiedPayloads()); //将ViewHolder以及与ItemHolderInfo一一对应,记录下来 mViewInfoStore.addToPreLayout(holder, animationInfo); if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved()&& !holder.shouldIgnore() && !holder.isInvalid()) { long key = getChangedHolderKey(holder); mViewInfoStore.addToOldChangeHolders(key, holder); } } } if (mState.mRunPredictiveAnimations) { //这里会遍历所有item,然后进行预布局 // 保存 item 的旧位置,然后LayoutManager可以进行布局映射 saveOldPositions(); final boolean didStructureChange = mState.mStructureChanged; mState.mStructureChanged = false; // temporarily disable flag because we are asking for previous layout mLayout.onLayoutChildren(mRecycler, mState); mState.mStructureChanged = didStructureChange; for (int i = 0; i < mChildHelper.getChildCount(); ++i) { final View child = mChildHelper.getChildAt(i); final ViewHolder viewHolder = getChildViewHolderInt(child); if (viewHolder.shouldIgnore()) { continue; } if (!mViewInfoStore.isInPreLayout(viewHolder)) { int flags = ItemAnimator.buildAdapterChangeFlagsForAnimations(viewHolder); boolean wasHidden = viewHolder .hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST); if (!wasHidden) { flags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT; } final ItemHolderInfo animationInfo = mItemAnimator.recordPreLayoutInformation( mState, viewHolder, flags, viewHolder.getUnmodifiedPayloads()); if (wasHidden) { recordAnimationInfoIfBouncedHiddenView(viewHolder, animationInfo); } else { mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo); } } } // we don't process disappearing list because they may re-appear in post layout pass. clearOldPositions(); } else { clearOldPositions(); } //通过计数来标记当前是否处于 Layout或 Scroll 状态,处于这两个状态时,一些动作是不允许的(比如更新 adapter 这个动作) //这里是减,与上面对应 onExitLayoutOrScroll(); resumeRequestLayout(false); //更新 Layout 状态,进入下一步 Layout mState.mLayoutStep = State.STEP_LAYOUT; }
总结下dispatchLayoutStep1()做的事情:
1. 处理 adapter 更新;
2. 决定是否需要跑动画;
3. 进行预布局;
接着看onMeasure()中的dispatchLayoutStep2()
RecyclerView.dispatchLayoutStep2()
private void dispatchLayoutStep2() { //********************************省略********************************* mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS); //重点:以LinearLayoutManager为例 mLayout.onLayoutChildren(mRecycler, mState); //********************************省略********************************* mState.mLayoutStep = State.STEP_ANIMATIONS;}
layout的第二步主要就是真正的去布局View了,RecyclerView的布局是由LayoutManager负责的,所以第二步的主要工作也都在LayoutManager中,由于每种布局的方式不一样,这里我们以常见的LinearLayoutManager为例:
LinearLayoutManage.onLayoutChildren()
@Override public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { //********************************省略********************************* mAnchorInfo.reset(); mAnchorInfo.mLayoutFromEnd = mShouldReverseLayout ^ mStackFromEnd; // 寻找anchor //首先寻找被focus的child,找到的话以此child作为anchor,否则根据布局的方向寻找最合适的child来作为anchor, //如果找到则将child的信息赋值给anchorInfo,其实anchorInfo主要记录的信息就是view的物理位置与在adapter中的位置。 //一般是 child 的第一个或最后一个。 updateAnchorInfoForLayout(recycler, state, mAnchorInfo); //********************************省略********************************* if (mAnchorInfo.mLayoutFromEnd) { firstLayoutDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_TAIL : LayoutState.ITEM_DIRECTION_HEAD; } else { firstLayoutDirection = mShouldReverseLayout ? LayoutState.ITEM_DIRECTION_HEAD : LayoutState.ITEM_DIRECTION_TAIL; } onAnchorReady(recycler, state, mAnchorInfo, firstLayoutDirection); detachAndScrapAttachedViews(recycler); mLayoutState.mInfinite = mOrientationHelper.getMode() == View.MeasureSpec.UNSPECIFIED; mLayoutState.mIsPreLayout = state.isPreLayout(); //从后往前布局 if (mAnchorInfo.mLayoutFromEnd) { //********************************省略********************************* fill(recycler, mLayoutState, state, false); //********************************省略********************************* } //从前往后布局 else { //********************************省略********************************* //无论从上到下还是从下到上布局,都调用的是fill方法,fill中有两个关键的方法: //一是layoutChunk(),负责添加add view 到RecyclerView中. //layoutChunk获取View的方法是通过调用RecyclerView.getViewForPosition()来获取相应的View(取缓存或新建); //二是recycleByLayoutState(),负责回收已经逃离出屏幕的View,recycleByLayoutState最终会调用Recycler。 //recycleViewHolderInternal()对View进行回收; fill(recycler, mLayoutState, state, false); //********************************省略********************************* } }
dispatchLayoutStep2大致过程:
- 找到anchor点
- 根据anchor一直向前布局,直至填充满anchor点前面的所有区域
- 根据anchor一直向后布局,直至填充满anchor点后面的所有区域
- anchor点的寻找是由updateAnchorInfoForLayout函数负责的:
接着看上面调用的那个fill(…)方法:
LinearLayoutManage.fill( )
int fill(RecyclerView.Recycler recycler, LayoutState layoutState, RecyclerView.State state, boolean stopOnFocusable) { final int start = layoutState.mAvailable; //********************************省略********************************* int remainingSpace = layoutState.mAvailable + layoutState.mExtra; LayoutChunkResult layoutChunkResult = mLayoutChunkResult; while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) { layoutChunkResult.resetInternal(); //对子 View进行布局 layoutChunk(recycler, state, layoutState, layoutChunkResult); if (layoutChunkResult.mFinished) { break; } //********************************省略********************************* if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) { layoutState.mScrollingOffset += layoutChunkResult.mConsumed; if (layoutState.mAvailable < 0) { layoutState.mScrollingOffset += layoutState.mAvailable; } //对 View进行回收 recycleByLayoutState(recycler, layoutState); } if (stopOnFocusable && layoutChunkResult.mFocusable) { break; } } return start - layoutState.mAvailable; }
fill中有两个关键的方法:一是layoutChunk(),负责添加add view 到RecyclerView中。二是recycleByLayoutState(),负责回收已经逃离出屏幕的View。
先来看layoutChunk():
LinearLayoutManage.layoutChunk()
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, LayoutState layoutState, LayoutChunkResult result) { //获取childView,这个与复用缓存有关,回头再看 View view = layoutState.next(recycler); if (view == null) { //********************************省略********************************* return; } LayoutParams params = (LayoutParams) view.getLayoutParams(); //********************************省略********************************* //这时会去测量childView的宽高,它会把 Margin以及ItemDecor考虑进去 measureChildWithMargins(view, 0, 0); result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view); int left, top, right, bottom; //垂直方向布局 if (mOrientation == VERTICAL) { //从右往左的布局,这里其实是根据布局方向以及ItemDecor计算childView 的四个顶点位置 if (isLayoutRTL()) { right = getWidth() - getPaddingRight(); left = right - mOrientationHelper.getDecoratedMeasurementInOther(view); } else { left = getPaddingLeft(); right = left + mOrientationHelper.getDecoratedMeasurementInOther(view); } if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { bottom = layoutState.mOffset; top = layoutState.mOffset - result.mConsumed; } else { top = layoutState.mOffset; bottom = layoutState.mOffset + result.mConsumed; } } //水平方向布局,类似 else { top = getPaddingTop(); bottom = top + mOrientationHelper.getDecoratedMeasurementInOther(view); if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { right = layoutState.mOffset; left = layoutState.mOffset - result.mConsumed; } else { left = layoutState.mOffset; right = layoutState.mOffset + result.mConsumed; } } //对childView 进行布局,会调用childView.layout(...) layoutDecorated(view, left + params.leftMargin, top + params.topMargin,right - params.rightMargin, bottom - params.bottomMargin); //********************************省略********************************* if (params.isItemRemoved() || params.isItemChanged()) { result.mIgnoreConsumed = true; } result.mFocusable = view.isFocusable(); }
接着看fill中第二个方法recycleByLayoutState:
LinearLayoutManager.recycleByLayoutState()
private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) { if (!layoutState.mRecycle) { return; } //RecyclerView向 position=0 滑动, 回收position=N if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { recycleViewsFromEnd(recycler, layoutState.mScrollingOffset); } //RecyclerView向 position=N 滑动, 回收position=0 else { recycleViewsFromStart(recycler, layoutState.mScrollingOffset); } }
recycleViewsFromEnd与recycleViewsFromStart两个方向实现类似,最后都是通过recycleChildren方法来回收 child,以recycleViewsFromEnd为例:
LinearLayoutManager.recycleViewsFromEnd()
private void recycleViewsFromEnd(RecyclerView.Recycler recycler, int dt) { final int childCount = getChildCount(); //********************************省略********************************* final int limit = mOrientationHelper.getEnd() - dt; //布局是否翻转,还记得构造 LinearLayoutManager 传入的 boolean 值嘛? if (mShouldReverseLayout) { for (int i = 0; i < childCount; i++) { View child = getChildAt(i); if (mOrientationHelper.getDecoratedStart(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) {// stop here recycleChildren(recycler, childCount - 1, i); return; } } } }
recycleViewsFromEnd判断了childView是否在 RecyclerView边界之外了,如果是就调用recycleChildren();
LinearLayoutManager.recycleChildren()
private void recycleChildren(RecyclerView.Recycler recycler, int startIndex, int endIndex) { if (startIndex == endIndex) { return; } // startIndex与endIndex代表着回收区间, 仅仅是回收顺序不同 ,取决于mShouldReverseLayout if (endIndex > startIndex) { for (int i = endIndex - 1; i >= startIndex; i--) { removeAndRecycleViewAt(i, recycler); } } else { for (int i = startIndex; i > endIndex; i--) { removeAndRecycleViewAt(i, recycler); } } }
在回收区间内,对每个 View调用removeAndRecycleViewAt();
LinearLayoutManager.removeAndRecycleViewAt()
public void removeAndRecycleViewAt(int index, Recycler recycler) { final View view = getChildAt(index); //这里会从 RecyclerVeiw中移除 childVeiw removeViewAt(index); //回收childView recycler.recycleView(view); }
看 View 是如何被回收的
Recycler.recycleView()
public void recycleView(View view) { ViewHolder holder = getChildViewHolderInt(view); //********************************省略********************************* recycleViewHolderInternal(holder); }
直接看recycleViewHolderInternal()的实现:
Recycler.recycleViewHolderInternal()
void recycleViewHolderInternal(ViewHolder holder) { //********************************省略********************************* final boolean transientStatePreventsRecycling = holder.doesTransientStatePreventRecycling(); final boolean forceRecycle = mAdapter != null && transientStatePreventsRecycling&& mAdapter.onFailedToRecycleView(holder); boolean cached = false; boolean recycled = false; if (forceRecycle || holder.isRecyclable()) { if (!holder.hasAnyOfTheFlags(ViewHolder.FLAG_INVALID | ViewHolder.FLAG_REMOVED| ViewHolder.FLAG_UPDATE)) { // mCachedViews是用来缓存ViewHolder的一个 list final int cachedViewSize = mCachedViews.size(); // mCachedViews的大小是否达到了上限值,mViewCacheMax默认大小是2 if (cachedViewSize == mViewCacheMax && cachedViewSize > 0) { //达到上限 recycleCachedViewAt(0); } //如果没有达到上限值,直接将 holder加入 mCachedViews中,上面recycleCachedViewAt()中有个移除操作,这时应该是没达到上限的 if (cachedViewSize < mViewCacheMax) { mCachedViews.add(holder); cached = true; } } mViewInfoStore.removeViewHolder(holder); if (!cached && !recycled && transientStatePreventsRecycling) { holder.mOwnerRecyclerView = null; } } }
如果mCachedViews这个 List的 size 没有达到上限(默认2),那么就很简单了,直接将要回收的 ViewHolder加入到mCachedViews中就完事了,
下面看看达到上限的情况,达到上限时,调用的是recycleCachedViewAt(),并传入了参数0:
Recycler.recycleCachedViewAt()
void recycleCachedViewAt(int cachedViewIndex) { //刚刚cachedViewIndex传入的是0,其实就是取出了最早放入mCachedViews中的那个 ViewHolder ViewHolder viewHolder = mCachedViews.get(cachedViewIndex); //加入 ViewHolder Pool 中 addViewHolderToRecycledViewPool(viewHolder); //从mCachedViews中移除ViewHolder mCachedViews.remove(cachedViewIndex); }
recycleCachedViewAt()做的就是在mCachedViews数量超过上限时将旧的 ViewHolder 加到 Pool中并从mCachedViews中移除,以保证新的 ViewHoler 能加入到mCachedViews中去。
那么看看旧的 ViewHolder 是如何加入到 Pool中去的。
Recycler.addViewHolderToRecycledViewPool()
void addViewHolderToRecycledViewPool(ViewHolder holder) { ViewCompat.setAccessibilityDelegate(holder.itemView, null); //如果回收监听器RecyclerListener不为空,这里会调用RecyclerListener的onViewRecycled()方法 dispatchViewRecycled(holder //将 ViewHoder RecyclerView指向置空 holder.mOwnerRecyclerView = null; //getRecycledViewPool()会获取当前 RecyclerView对象所对应的RecycledViewPool对象 getRecycledViewPool().putRecycledView(holder); }
RecycledViewPool干什么了?
public void putRecycledView(ViewHolder scrap) { final int viewType = scrap.getItemViewType(); final ArrayList scrapHeap = getScrapHeapForType(viewType); //如果此种 type缓存的ViewHolder 数量超过上限就不再缓存了 if (mMaxScrap.get(viewType) <= scrapHeap.size()) { return; } scrap.resetInternal(); scrapHeap.add(scrap); } //根据 viewType 获取对应viewType的ArrayList<ViewHolder> private ArrayList<ViewHolder> getScrapHeapForType(int viewType) { //mScrap是一个SparseArray<ArrayList<ViewHolder>> 对象,其实就是一个viewType与ArrayList<ViewHolder>一一对应的 Map; ArrayList<ViewHolder> scrap = mScrap.get(viewType); if (scrap == null) { scrap = new ArrayList<ViewHolder>(); mScrap.put(viewType, scrap); //mMaxScrap是一个SparseIntArray对象,它保存了每种viewType对应的缓存ViewHolder的上限数量(默认是5) if (mMaxScrap.indexOfKey(viewType) < 0) { mMaxScrap.put(viewType, DEFAULT_MAX_SCRAP); } } return scrap; }
到此RecylerView的回收机制算是分析完了,其实 RecyclerView 的回收机制有点像”二级缓存”,首先是mCachedViews(ArrayList),如果mCachedViews满了(上限为2+1(SDK>=21)),会把mCachedViews中的最前面的 ViewHolder 根据 type放入对应 Pool中,每种 type 对应的 Pool容量上限默认为5,超过了就不再缓存了,将超出的放入 Pool中后,最新待回收的 ViewHolder 会放入mCachedViews中。那么mCachedViews与Pool最大的区别在那呢?其实最大的区别就是复用时的原则,mCachedViews是根据 Position进行复用的,而 Pool是根据 type 进行复用的。
分析完了缓存策略,那么来简单的看看是如何取用的。还记得上面在讲 LinearLayoutManage.layoutChunk() 时调用的一个方法嘛?
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,LayoutState layoutState, LayoutChunkResult result) { //获取childView View view = layoutState.next(recycler); //********************************省略********************************* }
就是这个next方法,LayoutState为LinearLayoutManage的一个静态内部类:
LayoutState.next()
View next(RecyclerView.Recycler recycler) { //如果mScrapList不为空,则从mScrapList中找,通过 Position匹配 //所有正在与 RecyclerView 分离(正在跑 removed动画)的 ViewHolder都会在mScrapList中 //eg:例如第10个 item 正在与RecyclerView分离,你又回到了第10个 item,那么就会从mScrapList中取,它们的位置是对应的 if (mScrapList != null) { return nextViewFromScrapList(); } final View view = recycler.getViewForPosition(mCurrentPosition); mCurrentPosition += mItemDirection; return view; }
Recycler
public View getViewForPosition(int position) { return getViewForPosition(position, false); }View getViewForPosition(int position, boolean dryRun) { //FOREVER_NS:永不超时 return tryGetViewHolderForPositionByDeadline(position, dryRun, FOREVER_NS).itemView; } @Nullable ViewHolder tryGetViewHolderForPositionByDeadline(int position, boolean dryRun, long deadlineNs) { if (position < 0 || position >= mState.getItemCount()) { throw new IndexOutOfBoundsException("Invalid item position " + position + "(" + position + "). Item count:" + mState.getItemCount()); } boolean fromScrapOrHiddenOrCache = false; ViewHolder holder = null; // 1. 从mChangedScrap找合适的 ViewHolder if (mState.isPreLayout()) { holder = getChangedScrapViewForPosition(position); fromScrapOrHiddenOrCache = holder != null; } // 2. 根据 Position从mAttachedScrap或 mHiddenViews 或 mCachedViews 中找ViewHolder if (holder == null) { holder = getScrapOrHiddenOrCachedHolderForPosition(position, dryRun); if (holder != null) { if (!validateViewHolderForOffsetPosition(holder)) { // recycle holder (and unscrap if relevant) since it can't be used if (!dryRun) { holder.addFlags(ViewHolder.FLAG_INVALID); if (holder.isScrap()) { removeDetachedView(holder.itemView, false); holder.unScrap(); } else if (holder.wasReturnedFromScrap()) { holder.clearReturnedFromScrapFlag(); } recycleViewHolderInternal(holder); } holder = null; } else { fromScrapOrHiddenOrCache = true; } } } if (holder == null) { final int offsetPosition = mAdapterHelper.findPositionOffset(position); if (offsetPosition < 0 || offsetPosition >= mAdapter.getItemCount()) { throw new IndexOutOfBoundsException("Inconsistency detected. Invalid item "+ "position " + position + "(offset:" + offsetPosition + ")."+ "state:" + mState.getItemCount()); } final int type = mAdapter.getItemViewType(offsetPosition); // 3. 根据id从mAttachedScrap或 mCachedViews 中找ViewHolder,mAdapter.hasStableIds()默认返回 false if (mAdapter.hasStableIds()) { holder = getScrapOrCachedViewForId(mAdapter.getItemId(offsetPosition),type, dryRun); if (holder != null) { holder.mPosition = offsetPosition; fromScrapOrHiddenOrCache = true; } } //4. 从mViewCacheExtension中获取中找ViewHolder,但mViewCacheExtension默认为 null //你可以实现ViewCacheExtensionrm抽象类,然后自定义些缓存策略,但基本不用。 //但是 RecyclerView缓存时不会把 View放到这里,需要自己实现 if (holder == null && mViewCacheExtension != null) { final View view = mViewCacheExtension.getViewForPositionAndType(this, position, type); if (view != null) { holder = getChildViewHolder(view); if (holder == null) { throw new IllegalArgumentException("getViewForPositionAndType returned a view which does not have a ViewHolder"); } else if (holder.shouldIgnore()) { throw new IllegalArgumentException("getViewForPositionAndType returned" + " a view that is ignored. You must call stopIgnoring before returning this view."); } } } //5. 根据type从mRecyclerPool中获取ViewHolder if (holder == null) { holder = getRecycledViewPool().getRecycledView(type); if (holder != null) { holder.resetInternal(); if (FORCE_INVALIDATE_DISPLAY_LIST) { invalidateDisplayListInt(holder); } } } if (holder == null) { long start = getNanoTime(); if (deadlineNs != FOREVER_NS&& !mRecyclerPool.willCreateInTime(type, start, deadlineNs)) { //如果超时直接返回 null return null; } //6. 我们熟悉的createViewHolder()方法 holder = mAdapter.createViewHolder(RecyclerView.this, type); } } //**************************省略******************** //下面主要是设置itemView的LayoutParams boolean bound = false; if (mState.isPreLayout() && holder.isBound()) { holder.mPreLayoutPosition = position; } else if (!holder.isBound() || holder.needsUpdate() || holder.isInvalid()) { if (DEBUG && holder.isRemoved()) { throw new IllegalStateException("Removed holder should be bound and it should" + " come here only in pre-layout. Holder: " + holder); } final int offsetPosition = mAdapterHelper.findPositionOffset(position); bound = tryBindViewHolderByDeadline(holder, offsetPosition, position, deadlineNs); } final ViewGroup.LayoutParams lp = holder.itemView.getLayoutParams(); final LayoutParams rvLayoutParams; if (lp == null) { rvLayoutParams = (LayoutParams) generateDefaultLayoutParams(); holder.itemView.setLayoutParams(rvLayoutParams); } else if (!checkLayoutParams(lp)) { rvLayoutParams = (LayoutParams) generateLayoutParams(lp); holder.itemView.setLayoutParams(rvLayoutParams); } else { rvLayoutParams = (LayoutParams) lp; } rvLayoutParams.mViewHolder = holder; rvLayoutParams.mPendingInvalidate = fromScrapOrHiddenOrCache && bound; return holder; }
获取ViewHolder 的流程如下:
1. 从mChangedScrap找合适的 ViewHolder;
2. 根据 Position从mAttachedScrap或 mHiddenViews 或 mCachedViews 中找ViewHolder;
3. 如果mAdapter.hasStableIds(),根据id从mAttachedScrap或 mCachedViews 中找ViewHolder;
4. 从mViewCacheExtension中获取中找ViewHolder,但mViewCacheExtension默认为 null;
5. 根据type从mRecyclerPool中获取ViewHolder;
6. 创建新的 ViewHolder,createViewHolder();
至此,dispatchLayoutStep2()终于讲完了,但这里留下几个问题。
1. mAttachedScrap是什么?(这是一个过度状态的 ViewHolder,多次 Measure)
2. mChangedScrap是什么?(这是在过度状态的被改变的VeiwHolder)
3. StableIds是什么?
dispatchLayoutStep2()结束了,那么 onMeasure()也结束了,下面看onLayout():
Recycler.onLayout()
TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG); dispatchLayout(); TraceCompat.endSection(); mFirstLayoutComplete = true;
Recycler.dispatchLayout()
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; } mState.mIsMeasuring = false; //onMeasure时中dispatchLayoutStep2跑完之后mState.mLayoutStep的状态已经成了State.STEP_ANIMATIONS if (mState.mLayoutStep == State.STEP_START) { dispatchLayoutStep1(); mLayout.setExactMeasureSpecsFrom(this); dispatchLayoutStep2(); } //如果在 onMeasure 之后 View的尺寸有变化,则重新测量 else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth() ||mLayout.getHeight()!= getHeight()) { mLayout.setExactMeasureSpecsFrom(this); dispatchLayoutStep2(); } else { mLayout.setExactMeasureSpecsFrom(this); } dispatchLayoutStep3(); }
Recycler.dispatchLayoutStep3()
private void dispatchLayoutStep3() { //********************************省略********************************* if (mState.mRunSimpleAnimations) { //找出当前需要改变的 ViewHolder,处理过渡动画 for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) { ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i)); if (holder.shouldIgnore()) { continue; } long key = getChangedHolderKey(holder); final ItemHolderInfo animationInfo = mItemAnimator.recordPostLayoutInformation(mState,holder); ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key); if (oldChangeViewHolder != null && !oldChangeViewHolder.shouldIgnore()) { // run 过渡动画 final boolean oldDisappearing = mViewInfoStore.isDisappearing(oldChangeViewHolder); final boolean newDisappearing = mViewInfoStore.isDisappearing(holder); if (oldDisappearing && oldChangeViewHolder == holder) { // run disappear animation instead of change mViewInfoStore.addToPostLayout(holder, animationInfo); } else { final ItemHolderInfo preInfo = mViewInfoStore.popFromPreLayout(oldChangeViewHolder); // we add and remove so that any post info is merged. mViewInfoStore.addToPostLayout(holder, animationInfo); ItemHolderInfo postInfo = mViewInfoStore.popFromPostLayout(holder); if (preInfo == null) { handleMissingPreInfoForChangeError(key, holder, oldChangeViewHolder); } else { animateChange(oldChangeViewHolder, holder, preInfo, postInfo,oldDisappearing, newDisappearing); } } } else { mViewInfoStore.addToPostLayout(holder, animationInfo); } } // 开始循环跑动画 mViewInfoStore.process(mViewInfoProcessCallback); } //********************************省略*********************************}
这一步是与dispatchLayoutStep1呼应的,此时由于子View都已完成布局,所以子View的信息都发生了变化。dispatchLayoutStep1出现的mItemAnimator 和mViewInfoStore再次登场,这次mItemAnimator调用的是 recordPostLayoutInformation,dispatchLayoutStep1调用的是recordPreLayoutInformation方法。
而mViewInfoStore调用的是addToPostLayout方法,dispatchLayoutStep1调用的是addToPreLayout方法,也就是真正布局之前的状态,而现在要记录布局之后的状态,addToPostLayout和第一步的addToPreLayout类似,不过这次info信息被赋值给了record的postInfo变量,这样,一个InfoRecord中就包含了布局前后(postInfo&preInfo)view的状态。
最后,mViewInfoStore调用了process方法,这个方法就是根据mViewInfoStore中的View信息,来执行动画逻辑。
接下来看 onDraw():
RecyclerView.onDraw()
@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); } }
RecyclerView的 onDraw方法比较简单,主要是画ItemDecoration,至于childView的Draw都交给了 child 自己的 onDraw()。
可以看看在上面的layoutChunk()方法中调用了layoutDecoratedWithMargins方法:
public void layoutDecoratedWithMargins(View child, int left, int top, int right, int bottom) { final LayoutParams lp = (LayoutParams) child.getLayoutParams(); final Rect insets = lp.mDecorInsets; //child 在 layout时,去掉了ItemDecoration的区域,只负责自己本身的 layout child.layout(left + insets.left + lp.leftMargin, top + insets.top + lp.topMargin,right - insets.right - lp.rightMargin, bottom - insets.bottom - lp.bottomMargin); }
关于ItemDecoration
- getItemOffsets中为outRect设置的4个方向的值,将被计算进所有 decoration的尺寸中,而这个尺寸,会被计入了RecyclerView每个item view的padding中;
- 在onDraw为divider设置绘制范围,并绘制到canvas上,而这个绘制范围可以超出在getItemOffsets中设置的范围,但由于decoration是绘制在child view的底下,所以并不可见,但是会存在 overdraw;
- decoration的onDraw,child view的onDraw,decoration的onDrawOver,这三者是依次发生的,onDrawOver是绘制在最上层的,所以它的绘制位置并不受限制;
- RecyclerView分析
- RecyclerView源码分析
- RecyclerView源码分析
- RecyclerView滑动源码分析
- Android RecyclerView源码分析
- RecyclerView机制分析: Recycler
- RecyclerView机制分析: State
- RecyclerView源码分析
- recyclerview的应用分析
- RecyclerView整体思路分析
- RecyclerView缓存分析
- RecyclerView源码分析
- RecyclerView源码分析
- RecyclerView源码分析
- RecyclerView滚动事件分析
- RecyclerView 源码分析
- RecyclerView回收原理分析
- RecyclerView源码分析
- opencv 库文件结构图
- spring BeanFactory与FactoryBean
- BeautifulSoup官网学习笔记
- Spark学习01---Maven Project
- 设计模式的6大原则
- RecyclerView分析
- Java面向对象01
- Java面向对象02
- Oracle_11g的服务启动与关闭
- Java模块 -- Logger日志的使用
- Java面向对象03
- Java面向对象04
- Java面向对象05
- < Kotlin > Android Studio3.0 新特性 ~ New Features in Android Studio Preview (译文)