RecyclerView源码详解(第三篇LayoutManager源码详解)

上两篇已经谈到RecyclerView布局子View的位置完全是交给LayoutManager的子类来实现,它不像ListView和GridView那样什么事情都自己处理,而把一些功能完全抽离出来交给客户端自己扩展,当然它也提供了类似ListView和GridView的布局管理器,如LinearLayoutManager线性布局,GridLayoutManager网格布局,瀑布流布局StaggeredGridLayoutManager。首先我们来看一下LayoutManager这个抽象类到底有几个抽象方法,再重写这个类的时候,我们必须实现抽象方法


generateDefaultLayoutParams() :这个方法获得子控件LayoutParams属性的时候默认调用,我们可以在这个方法里扩展一下我们自己新加的控件属性

在源码中只找到这一个抽象方法,麻痹,难道重写它的时候就只实现这个方法吗?尝试了一下果然不行,子View都不知道死哪去了
public void setLayoutManager(LayoutManager layout) {
        if (layout == mLayout) {
            return;
        }
        stopScroll();
        // TODO We should do this switch a dispatchLayout pass and animate children. There is a good
        // chance that LayoutManagers will re-use views.
        if (mLayout != null) {
            // end all running animations
            if (mItemAnimator != null) {
                mItemAnimator.endAnimations();
            }
            mLayout.removeAndRecycleAllViews(mRecycler);
            mLayout.removeAndRecycleScrapInt(mRecycler);
            mRecycler.clear();

            if (mIsAttached) {
                mLayout.dispatchDetachedFromWindow(this, mRecycler);
            }
            mLayout.setRecyclerView(null);
            mLayout = null;
        } else {
            mRecycler.clear();
        }
        // this is just a defensive measure for faulty item animators.
        mChildHelper.removeAllViewsUnfiltered();
        mLayout = layout;
        if (layout != null) {
            if (layout.mRecyclerView != null) {
                throw new IllegalArgumentException("LayoutManager " + layout +
                        " is already attached to a RecyclerView: " + layout.mRecyclerView);
            }
            mLayout.setRecyclerView(this);
            if (mIsAttached) {
                mLayout.dispatchAttachedToWindow(this);
            }
        }
        mRecycler.updateViewCacheSize();
        requestLayout();
    }

这个方法是为RecyclerView设置LayoutManager的,在最后一行惊喜的发现 requestLayout(),这是什么,每次设置了LayoutManager之后就会执行View树的重绘,想到重绘的三部曲不就是onMeasure、onLayout、onDraw吗,ok先看一下RecycleView的onMeasure方法
 
 
@Override
	protected void onMeasure(int widthSpec, int heightSpec) {
		// 正在测量的时候数据通知改变
		if (mAdapterUpdateDuringMeasure) {
			eatRequestLayout();
			processAdapterUpdatesAndSetAnimationFlags();
			/**
			 * 如果设置了动画的话mState.mInPreLayout为true
			 */
			if (mState.mRunPredictiveAnimations) {
				// 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;
			resumeRequestLayout(false);
		}

		if (mAdapter != null) {
			// 为状态添加子View的数量
			mState.mItemCount = mAdapter.getItemCount();
		} else {
			mState.mItemCount = 0;
		}
		// 假如布局文件为空,那么采用默认测量
		if (mLayout == null) {
			defaultOnMeasure(widthSpec, heightSpec);
		} else {
			// 否则用布局文件测量
			mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);
		}

		mState.mInPreLayout = false; // clear
	}

在方法的最后可以看到,如果mLayout 不为null,就会用mLayout.onMeasure 方法进行测量,如果没有,调用默认的方法测量,也就是我们可以重写LayoutManager的
onMeasure方法进行测量子view(不过这时还没有调用CreateHodler所以没有子View,所以一般此方法不用重写),那么接下来就是RecyclerView的onLayout方法了
 
@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		eatRequestLayout();
		// TraceView的速度测量
		TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG);
		dispatchLayout();
		TraceCompat.endSection();
		resumeRequestLayout(false);
		// 第一次布局完成时
		mFirstLayoutComplete = true;
	}

这个方法较为简洁,都是调用的其他方法,布局的调用方法是在dispatchLayout方法中
 
void dispatchLayout() {
		// 没有adapter和mLayout直接返回
		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;
		}
		// 储存动画信息的类

		mViewInfoStore.clear();
		eatRequestLayout();
		// 布局和滚动的累加器
		onEnterLayoutOrScroll();
		// 设置一些必要的参数
		processAdapterUpdatesAndSetAnimationFlags();
		mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations
				&& mItemsChanged;
		mItemsAddedOrRemoved = mItemsChanged = false;
		// 如果有动画的话mInPreLayout为true
		mState.mInPreLayout = mState.mRunPredictiveAnimations;
		mState.mItemCount = mAdapter.getItemCount();
		findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);
		mState.mItemCount = mAdapter.getItemCount();
		//省略若干行….

		// Step 2: Run layout
		mState.mInPreLayout = false;
		mLayout.onLayoutChildren(mRecycler, mState);
//省略若干行…..
	}
 

dispatchLayout方法代码量相当的多,所以省略了一些itemView动画参数的判断和处理,也省略了滚动到那个位置的参数清空处理等,很明显,最后的代码调用了LayoutManager的onLayoutChildren方法进行子View的位置排版,由于onMeasure的时候子View还不存在,所以测量和布局都会交给onLayoutChildren来处理,下面以LinearLayoutManager方法为例


public void onLayoutChildren(RecyclerView.Recycler recycler,
			RecyclerView.State state) {
		省略若干行代码….
		if (mPendingSavedState != null || mPendingScrollPosition != NO_POSITION) {
			if (state.getItemCount() == 0) {
//移除所有的子View
				removeAndRecycleAllViews(recycler);
				return;
			}
		}
		省略若干行代码….
		mAnchorInfo.reset();
		mAnchorInfo.mLayoutFromEnd = mShouldReverseLayout ^ mStackFromEnd;
		// calculate anchor position and coordinate
		// 得到锚点
		updateAnchorInfoForLayout(recycler, state, mAnchorInfo);
		
		int startOffset;
		int endOffset;
		onAnchorReady(recycler, state, mAnchorInfo);
		//回收所有的子View进scrapView
		detachAndScrapAttachedViews(recycler);
		mLayoutState.mIsPreLayout = state.isPreLayout();
		if (mAnchorInfo.mLayoutFromEnd) {
			// fill towards start
			updateLayoutStateToFillStart(mAnchorInfo);
			mLayoutState.mExtra = extraForStart;
//填充子View
			fill(recycler, mLayoutState, state, false);
			startOffset = mLayoutState.mOffset;
			final int firstElement = mLayoutState.mCurrentPosition;
			if (mLayoutState.mAvailable > 0) {
				extraForEnd += mLayoutState.mAvailable;
			}
			// fill towards end
			updateLayoutStateToFillEnd(mAnchorInfo);
			mLayoutState.mExtra = extraForEnd;
			mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
			fill(recycler, mLayoutState, state, false);
			endOffset = mLayoutState.mOffset;

			if (mLayoutState.mAvailable > 0) {
				// end could not consume all. add more items towards start
				extraForStart = mLayoutState.mAvailable;
				updateLayoutStateToFillStart(firstElement, startOffset);
				mLayoutState.mExtra = extraForStart;
				fill(recycler, mLayoutState, state, false);
				startOffset = mLayoutState.mOffset;
			}
		} else {
			// fill towards end
			updateLayoutStateToFillEnd(mAnchorInfo);
			mLayoutState.mExtra = extraForEnd;
			fill(recycler, mLayoutState, state, false);
			endOffset = mLayoutState.mOffset;
			final int lastElement = mLayoutState.mCurrentPosition;
			if (mLayoutState.mAvailable > 0) {
				extraForStart += mLayoutState.mAvailable;
			}
			// fill towards start
			updateLayoutStateToFillStart(mAnchorInfo);
			mLayoutState.mExtra = extraForStart;
			mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
			fill(recycler, mLayoutState, state, false);
			startOffset = mLayoutState.mOffset;

			if (mLayoutState.mAvailable > 0) {
				extraForEnd = mLayoutState.mAvailable;
				// start could not consume all it should. add more items towards
				// end
				updateLayoutStateToFillEnd(lastElement, endOffset);
				mLayoutState.mExtra = extraForEnd;
				fill(recycler, mLayoutState, state, false);
				endOffset = mLayoutState.mOffset;
			}
		}
省略若干行…
		
	}


这个方法做的最主要工作就是调用detachAndScrapAttachedViews回收以前的itemView,updateAnchorInfoForLayout方法寻找锚点,也就是参考坐标,假如以屏幕上方为参考坐
标的话,那么子View就从屏幕上方开始填入RecyclerView,如果在下方则
从底部开始填充,当然RecycleView刚开始显示的时候,默认是从屏幕上面开始填充的,锚点也有可能是在屏幕任何一个位置,比如我调用scrollToPosition
方法滚动哪一个位置,那么这个子View将在下一次的Onlayout充当锚点,以它为基础向上布局,向下布局,找到锚点后就是向上或向下填充子view了,填充调用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.SCOLLING_OFFSET_NaN) {
			// TODO ugly bug fix. should not happen
			if (layoutState.mAvailable < 0) {
				layoutState.mScrollingOffset += layoutState.mAvailable;
			}
			//回收超过边界的View
			recycleByLayoutState(recycler, layoutState);
		}
		int remainingSpace = layoutState.mAvailable + layoutState.mExtra;
		LayoutChunkResult layoutChunkResult = new LayoutChunkResult();
		while (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.SCOLLING_OFFSET_NaN) {
				layoutState.mScrollingOffset += layoutChunkResult.mConsumed;
				if (layoutState.mAvailable < 0) {
					//最后一个view离开屏幕的偏移量
					layoutState.mScrollingOffset += layoutState.mAvailable;
				}
				recycleByLayoutState(recycler, layoutState);
			}
			if (stopOnFocusable && layoutChunkResult.mFocusable) {
				break;
			}
		}
		if (DEBUG) {
			validateChildOrder();
		}
		//返回实际占用了多少距离,只显示一部分的
		return start - layoutState.mAvailable;
	}


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.SCOLLING_OFFSET_NaN) {
			// TODO ugly bug fix. should not happen
			if (layoutState.mAvailable < 0) {
				layoutState.mScrollingOffset += layoutState.mAvailable;
			}
			//回收超过边界的View
			recycleByLayoutState(recycler, layoutState);
		}
		int remainingSpace = layoutState.mAvailable + layoutState.mExtra;
		LayoutChunkResult layoutChunkResult = new LayoutChunkResult();
		while (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.SCOLLING_OFFSET_NaN) {
				layoutState.mScrollingOffset += layoutChunkResult.mConsumed;
				if (layoutState.mAvailable < 0) {
					//最后一个view离开屏幕的偏移量
					layoutState.mScrollingOffset += layoutState.mAvailable;
				}
				recycleByLayoutState(recycler, layoutState);
			}
			if (stopOnFocusable && layoutChunkResult.mFocusable) {
				break;
			}
		}
		if (DEBUG) {
			validateChildOrder();
		}
		//返回实际占用了多少距离,只显示一部分的
		return start - layoutState.mAvailable;
	}

首先获得屏幕还有多少剩余空间remainingSpace可用,根据这个值不断的减去子View所占用的空间大小,当这个值小于0的时候那么,布局子View结束,如果当前所有子View满足还没有超过remainingSpace时调用layoutChunk安排下一个view的位置
 
 
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
			LayoutState layoutState, LayoutChunkResult result) {
		View view = layoutState.next(recycler);
		if (view == null) {
			if (DEBUG && layoutState.mScrapList == null) {
				throw new RuntimeException("received null view when unexpected");
			}
			// if we are laying out views in scrap, this may return null which
			// means there is
			// no more items to layout.
			result.mFinished = true;
			return;
		}
		LayoutParams params = (LayoutParams) view.getLayoutParams();
		if (layoutState.mScrapList == null) {
			if (mShouldReverseLayout == (layoutState.mLayoutDirection == LayoutState.LAYOUT_START)) {
				addView(view);
			} else {
				addView(view, 0);
			}
		} else {
			if (mShouldReverseLayout == (layoutState.mLayoutDirection == LayoutState.LAYOUT_START)) {
				addDisappearingView(view);
			} else {
				addDisappearingView(view, 0);
			}
		}
		//测量子View、
		measureChildWithMargins(view, 0, 0);
		//result.mConsumed=子View总够需要的空间
		result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);
		int left, top, right, bottom;
		if (mOrientation == VERTICAL) {
			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();
			//需加上分割线的距离定义top的位置
			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;
			}
		}
		// We calculate everything with View's bounding box (which includes
		// decor and margins)
		// To calculate correct layout position, we subtract margins.
		layoutDecorated(view, left + params.leftMargin, top + params.topMargin,
				right - params.rightMargin, bottom - params.bottomMargin);
		if (DEBUG) {
			Log.d(TAG, "laid out child at position " + getPosition(view)
					+ ", with l:" + (left + params.leftMargin) + ", t:"
					+ (top + params.topMargin) + ", r:"
					+ (right - params.rightMargin) + ", b:"
					+ (bottom - params.bottomMargin));
		}
		// Consume the available space if the view is not removed OR changed
		if (params.isItemRemoved() || params.isItemChanged()) {
			result.mIgnoreConsumed = true;
		}
		result.mFocusable = view.isFocusable();
	}

这个方法首先通过layoutState.next获取到下一个子View,然后通过addView将子view添加到RecyclerView中,然后通过measureChildWithMargins测量子view的大小,然后计算子
view的left、top、right、bottom,最后layoutDecorated实现子view的位置排列。那么先看一下怎么通过适配器获得子View的
 
View next(RecyclerView.Recycler recycler) {
			if (mScrapList != null) {
				return nextViewFromScrapList();
			}
			final View view = recycler.getViewForPosition(mCurrentPosition);
			mCurrentPosition += mItemDirection;
			return view;
		}
首先通过判断mScrapList 集合中是否有缓存有的话就用mScrapList里面的数据,这个缓存主要用于onLayout被调用多次时用的,假如第一次没有子view,那么创建的子view会
被添加到mScrapList中,第二次的时候不会创建子view,而是直接取缓存获得速度。也就是说这个缓存只在onlayout中能用到,如果没有数据,那么调用getViewForPosition
获得子view
 
View getViewForPosition(int position, boolean dryRun) {
			if (position < 0 || position >= mState.getItemCount()) {
				throw new IndexOutOfBoundsException("Invalid item position "
						+ position + "(" + position + "). Item count:"
						+ mState.getItemCount());
			}
			boolean fromScrap = false;
			ViewHolder holder = null;
			// 0) If there is a changed scrap, try to find from there
			if (mState.isPreLayout()) {
				holder = getChangedScrapViewForPosition(position);
				fromScrap = holder != null;
			}
			// 1) Find from scrap by position
			if (holder == null) {
				holder = getScrapViewForPosition(position, INVALID_TYPE, dryRun);
				if (holder != null) {
					if (!validateViewHolderForOffsetPosition(holder)) {
						// recycle this scrap
						if (!dryRun) {
							// we would like to recycle this but need to make
							// sure it is not used by
							// animation logic etc.
							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 {
						fromScrap = 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);
				// 2) Find from scrap via stable ids, if exists
				if (mAdapter.hasStableIds()) {
					holder = getScrapViewForId(
							mAdapter.getItemId(offsetPosition), type, dryRun);
					if (holder != null) {
						// update position
						holder.mPosition = offsetPosition;
						fromScrap = true;
					}
				}
				if (holder == null && mViewCacheExtension != null) {
					// We are NOT sending the offsetPosition because
					// LayoutManager does not
					// know it.
					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.");
						}
					}
				}
				if (holder == null) { // fallback to recycler
					// try recycler.
					// Head to the shared pool.
					if (DEBUG) {
						Log.d(TAG, "getViewForPosition(" + position
								+ ") fetching from shared " + "pool");
					}
					holder = getRecycledViewPool().getRecycledView(type);
					if (holder != null) {
						holder.resetInternal();
						if (FORCE_INVALIDATE_DISPLAY_LIST) {
							invalidateDisplayListInt(holder);
						}
					}
				}
				if (holder == null) {
					holder = mAdapter.createViewHolder(RecyclerView.this, type);
					if (DEBUG) {
						Log.d(TAG, "getViewForPosition created new ViewHolder");
					}
				}
			}

			// This is very ugly but the only place we can grab this information
			// before the View is rebound and returned to the LayoutManager for
			// post layout ops.
			// We don't need this in pre-layout since the VH is not updated by
			// the LM.
			if (fromScrap
					&& !mState.isPreLayout()
					&& holder
							.hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST)) {
				holder.setFlags(0, ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);
				if (mState.mRunSimpleAnimations) {
					int changeFlags = ItemAnimator
							.buildAdapterChangeFlagsForAnimations(holder);
					changeFlags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;
					final ItemHolderInfo info = mItemAnimator
							.recordPreLayoutInformation(mState, holder,
									changeFlags, holder.getUnmodifiedPayloads());
					recordAnimationInfoIfBouncedHiddenView(holder, info);
				}
			}

			boolean bound = false;
			if (mState.isPreLayout() && holder.isBound()) {
				// do not update unless we absolutely have to.
				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);
				holder.mOwnerRecyclerView = RecyclerView.this;
				mAdapter.bindViewHolder(holder, offsetPosition);
				attachAccessibilityDelegate(holder.itemView);
				bound = true;
				if (mState.isPreLayout()) {
					holder.mPreLayoutPosition = position;
				}
			}

			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 = fromScrap && bound;
			return holder.itemView;
		}

 
这个方法的代码量很大,但主题意思不难看懂,首先从mAttachedScrap缓存集合中获取,如果有的话,如果没有的话从mViewCacheExtension集合中获取,如果还没有的话,
从RecycledViewPool缓存中获取,如果还是没有的话,那么只能通过mAdapter.createViewHolder创建了。从中可以看出RecyclerView总共用了四级缓存
mScrapList缓存:在onLayout中使用
mAttachedScrap:在滑动的时候缓存view
mViewCacheExtension:用户扩展缓存
RecycledViewPool:在滑动的时候使用,配合mAttachedScrap


看到这可以看出,如果想自定义LayoutManager的话,那么必须重写onLayoutChildren方法布局子view,还不要忘了将子view在合适的时机加入的缓存中。最后看一下在手指滑动
的时候LayoutManager需要做一些什么操作,那么进入RecyclerView的onTouchEvent里看一下
 
 
public boolean onTouchEvent(MotionEvent e) {
		final boolean canScrollHorizontally = mLayout.canScrollHorizontally();
		final boolean canScrollVertically = mLayout.canScrollVertically();
省略若干行…..
		switch (action) {
		case MotionEvent.ACTION_MOVE: {
			final int index = MotionEventCompat.findPointerIndex(e,
					mScrollPointerId);
			省略若干行…..
				if (scrollByInternal(canScrollHorizontally ? dx : 0,
						canScrollVertically ? dy : 0, vtev)) {
					getParent().requestDisallowInterceptTouchEvent(true);
				} {
					省略若干行…..
			}
			省略若干行…..
			break;
		case MotionEvent.ACTION_UP: {
		省略若干行…..
			if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
				setScrollState(SCROLL_STATE_IDLE);
			}
			resetTouch();
		}
			break;
		}
		省略若干行…..
		return true;
	}

这个方法代码将多,这里只留最主要的代码,可以看出这里回调了mLayout.canScrollHorizontally()和mLayout.canScrollVertically(),也就是说在重写LayoutManager的方法
时,如果我们想左右滑动就将canScrollHorizontally的返回值设为true,同理想垂直滑动的话canScrollVertically必须也得返回true。如果满足一些列条件的话
滑动最后将交给scrollByInternal方法处理,手指抬起时如果认为是快速滑动的话,那么会调用fling方法进行移动,那么先看一下scrollByInternal方法
 
boolean scrollByInternal(int x, int y, MotionEvent ev) {
		int unconsumedX = 0, unconsumedY = 0;
		int consumedX = 0, consumedY = 0;

		consumePendingUpdateOperations();
		if (mAdapter != null) {
			eatRequestLayout();
			onEnterLayoutOrScroll();
			TraceCompat.beginSection(TRACE_SCROLL_TAG);
			if (x != 0) {
				consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
				unconsumedX = x - consumedX;
			}
			if (y != 0) {
				consumedY = mLayout.scrollVerticallyBy(y, mRecycler, mState);
				unconsumedY = y - consumedY;
			}
			TraceCompat.endSection();
			repositionShadowingViews();
			onExitLayoutOrScroll();
			resumeRequestLayout(false);
		}
		if (!mItemDecorations.isEmpty()) {
			invalidate();
		}

		if (dispatchNestedScroll(consumedX, consumedY, unconsumedX,
				unconsumedY, mScrollOffset)) {
			// Update the last touch co-ords, taking any scroll offset into
			// account
			mLastTouchX -= mScrollOffset[0];
			mLastTouchY -= mScrollOffset[1];
			if (ev != null) {
				ev.offsetLocation(mScrollOffset[0], mScrollOffset[1]);
			}
			mNestedOffsets[0] += mScrollOffset[0];
			mNestedOffsets[1] += mScrollOffset[1];
		} else if (ViewCompat.getOverScrollMode(this) != ViewCompat.OVER_SCROLL_NEVER) {
			if (ev != null) {
				pullGlows(ev.getX(), unconsumedX, ev.getY(), unconsumedY);
			}
			considerReleasingGlowsOnScroll(x, y);
		}
		if (consumedX != 0 || consumedY != 0) {
			dispatchOnScrolled(consumedX, consumedY);
		}
		if (!awakenScrollBars()) {
			invalidate();
		}
		return consumedX != 0 || consumedY != 0;
	}

这个方法最终调用了如果是水平滑动的话调用mLayout.scrollHorizontallyBy(x, mRecycler, mState),如果是垂直滑动的话调用了mLayout.scrollVerticallyBy,所以最终
滑动多少距离由LayoutManager实现,那么你想实现左右滑动就重写scrollHorizontallyBy方法,如果实现垂直滑动就重写scrollVerticallyBy方法实现看下LinearLayoutManager
怎么实现的
 
 
@Override
	public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
			RecyclerView.State state) {
		if (mOrientation == HORIZONTAL) {
			return 0;
		}
		return scrollBy(dy, recycler, state);
	}


 
 
int scrollBy(int dy, RecyclerView.Recycler recycler,
			RecyclerView.State state) {
		if (getChildCount() == 0 || dy == 0) {
			return 0;
		}
		mLayoutState.mRecycle = true;
		ensureLayoutState();
		final int layoutDirection = dy > 0 ? LayoutState.LAYOUT_END
				: LayoutState.LAYOUT_START;
		final int absDy = Math.abs(dy);
		updateLayoutState(layoutDirection, absDy, true, state);
		final int freeScroll = mLayoutState.mScrollingOffset;
		final int consumed = freeScroll
				+ fill(recycler, mLayoutState, state, false);
		if (consumed < 0) {
			if (DEBUG) {
				Log.d(TAG, "Don't have any more elements to scroll");
			}
			return 0;
		}
		final int scrolled = absDy > consumed ? layoutDirection * consumed : dy;
	    mOrientationHelper.offsetChildren(-scrolled);
		if (DEBUG) {
			Log.d(TAG, "scroll req: " + dy + " scrolled: " + scrolled);
		}
		mLayoutState.mLastScrollDelta = scrolled;
		return scrolled;
	}

这两个方法主要做了三件事,通过updateLayoutState修正一些状态,比如锚点在哪,是否有itemView的动画等,通过fill先检查哪些子view超出边界进行回收,然后重新填充
新的子view,通过offsetChildren改变所有子view的top or bottom的值从而达到移动的效果。
 
 



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值