RecyclerView原理解析

转载请注明:https://blog.csdn.net/feather_wch/article/details/79932523

参考和学习资料

  1. 自定义LayoutManager

  2. RecyclerView绘制流程解析

  3. LinearLayoutManager填充、测量、布局过程

  4. 掌握自定义LayoutManager(一) 系列开篇 常见误区、问题、注意事项,常用API

  5. 把RecyclerView撸成 马 蜂 窝

  6. 深入了解RecyclerView预布局状态(preLayout)

  7. preLayout\postLayout

1、LayoutManager的作用

  1. layout(布局)子视图
  2. 滚动过程中根据子视图布局中所处的位置,决定何时添加和回收子视图
  3. 滚动子视图。

2、RecyclerView与LayoutManager的联系

  1. RV设置LayoutManager会调用requestLayout()进行View树的重绘,最终会走RV的测量、布局、绘制三大流程。
  2. RV子View的测量、布局最终都在布局阶段交由LayoutManageronLayoutChildren完成

3、LinearLayoutManager的主要方法

方法解释
onLayoutChildrenLayoutManager的主入口,在初始化布局时调用。如果Adapter数据改变或者Adapter被替换时,会再次调用。作用:初始化时放置Item,直至填满布局为止。
canScrollHorizontally() & canScrollVertically()当想要滚动的方向,与当前布局方向相同时,在对应方法返回true,另一个返回false。
scrollHorizontallyBy() & scrollVerticallyBy()RecyclerView已经处理了触摸事件,会将相应的偏移值(dx/dy)传入这两个方法中的对应方法。根据偏移值需要完成三件事:1、将所有Item视图移动到适当位置 2、决定移动视图后 添加/移除 视图。 3、返回滚动的实际距离。框架会根据它判断你是否触碰到边界。

4、LinearLayoutManager的滚动方法源码分析。

 /**============================================== * RecyclerView的三种状态: 1-开始;2-布局;3-动画 *==============================================*/ public static class State { static final int STEP_START = 1; static final int STEP_LAYOUT = 1 << 1; static final int STEP_ANIMATIONS = 1 << 2; //xxx } /**================================================ * 1、是否支持水平/垂直滚动 * 1. 如果可以水平滚动,canScrollHorizontally()就返回true * 2. 如果可以垂直滚动,canScrollVertically()就返回true * @return *=============================================*/ @Override public boolean canScrollHorizontally() { return mOrientation == HORIZONTAL; } @Override public boolean canScrollVertically() { return mOrientation == VERTICAL; } /** * 2、处理滚动事件. * RecyclerView处理滚动事件后,会调用对应方向的方法,并传入dx/dy偏移值。 */ @Override public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) { if (mOrientation == VERTICAL) { return 0; } return scrollBy(dx, recycler, state); } @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) { //1. 没有子元素直接返回 if (getChildCount() == 0 || dy == 0) { return 0; } //2. 暂定需要“回收” mLayoutState.mRecycle = true; //3. 创建mLayoutState。并且创建mOrientationHelper ensureLayoutState(); //4. dy>0-LAYOUT_END: 表示向下翻滚到末尾(或者向右翻滚到最右侧), LAYOUT_START相反 final int layoutDirection = dy > 0 ? LinearLayoutManager.LayoutState.LAYOUT_END : LinearLayoutManager.LayoutState.LAYOUT_START; //5. 均用正数进行计算 final int absDy = Math.abs(dy); //6. 更新布局状态 updateLayoutState(layoutDirection, absDy, true, state); //7. consumed = 不添加新View情况下可以滑动的距离 + 添加View所增加的像素数 final int consumed = mLayoutState.mScrollingOffset + fill(recycler, mLayoutState, state, false); if (consumed < 0) { return 0; } final int scrolled = absDy > consumed ? layoutDirection * consumed : dy; mOrientationHelper.offsetChildren(-scrolled); mLayoutState.mLastScrollDelta = scrolled; //8. 实际滑动的距离 return scrolled; } /** * 更新布局状态 */ private void updateLayoutState(int layoutDirection, int requiredSpace, boolean canUseExistingSpace, RecyclerView.State state) { //1-用于在View的放置数量没有限制的情况中 mLayoutState.mInfinite = resolveIsInfinite(); //2-用于你想要在item可见前提前布局item mLayoutState.mExtra = getExtraLayoutSpace(state); //3-滑动的方向 mLayoutState.mLayoutDirection = layoutDirection; int scrollingOffset; if (layoutDirection == LinearLayoutManager.LayoutState.LAYOUT_END) { mLayoutState.mExtra += mOrientationHelper.getEndPadding(); final View child = getChildClosestToEnd(); //方向上第一个child //4-遍历数据适配器的方向(遍历children的方向) mLayoutState.mItemDirection = mShouldReverseLayout ? LinearLayoutManager.LayoutState.ITEM_DIRECTION_HEAD : LinearLayoutManager.LayoutState.ITEM_DIRECTION_TAIL; //5-当前应该从adapter中获取Item的position mLayoutState.mCurrentPosition = getPosition(child) + mLayoutState.mItemDirection; //6-布局开始位置的像素偏移(添加布局时需要通过这个确定子View的位置) mLayoutState.mOffset = mOrientationHelper.getDecoratedEnd(child); scrollingOffset = mOrientationHelper.getDecoratedEnd(child) - mOrientationHelper.getEndAfterPadding(); } else { xxx } //7-布局方向上需要填充的像素数量(并减去 (View的最右侧-View不包含爬到顶的最右侧)) mLayoutState.mAvailable = requiredSpace; if (canUseExistingSpace) { mLayoutState.mAvailable -= scrollingOffset; } //8-在不创建新View的情况下,我们可以滚动的距离(独立于布局) mLayoutState.mScrollingOffset = scrollingOffset; } int fill(RecyclerView.Recycler recycler, LinearLayoutManager.LayoutState layoutState, RecyclerView.State state, boolean stopOnFocusable) { // max offset we should set is mFastScroll + available final int start = layoutState.mAvailable; //1-根据布局状态回收不再显示的子View if (layoutState.mScrollingOffset != LinearLayoutManager.LayoutState.SCROLLING_OFFSET_NaN) { xxx recycleByLayoutState(recycler, layoutState); } int remainingSpace = layoutState.mAvailable + layoutState.mExtra; LinearLayoutManager.LayoutChunkResult layoutChunkResult = mLayoutChunkResult; //2-(无限个Item 或者 有多余的空间) 并且 Data Adapater还有更多的Item。 // layoutState.mExtra就是整个Recyclerview的宽度或者0, while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) { layoutChunkResult.resetInternal(); //3-添加View layoutChunk(recycler, state, layoutState, layoutChunkResult); xxx } //4-添加View所增加的像素数 return start - layoutState.mAvailable; } /** * 根据布局方向调用合适的回收方法 * @param recycler * @param layoutState */ private void recycleByLayoutState(RecyclerView.Recycler recycler, LayoutState layoutState) { if (!layoutState.mRecycle || layoutState.mInfinite) { return; } //1-上滑到最顶端or滑动到最左端。从另一头开始回收 if (layoutState.mLayoutDirection == LayoutState.LAYOUT_START) { recycleViewsFromEnd(recycler, layoutState.mScrollingOffset); } else { recycleViewsFromStart(recycler, layoutState.mScrollingOffset); } } /** * 从末端开始视图回收 * @param recycler * @param dt */ private void recycleViewsFromEnd(RecyclerView.Recycler recycler, int dt) { //1-RecyclerView当前可见的Item数量 final int childCount = getChildCount(); //2-RV末端不包含padding的位置 - 在不创建新View可以滑动的最大距离 final int limit = mOrientationHelper.getEnd() - dt; //3-是否是反向遍历布局 if (mShouldReverseLayout) { for (int i = 0; i < childCount; i++) { View child = getChildAt(i); if (mOrientationHelper.getDecoratedStart(child) < limit || mOrientationHelper.getTransformedStartWithDecoration(child) < limit) { // stop here recycleChildren(recycler, 0, i); return; } } } else { //4-(正常顺序的处理情况下)遍历当前可见的ItemView, 此时左滑,因此右面的View可能要回收 for (int i = childCount - 1; i >= 0; i--) { View child = getChildAt(i); //5-当Child View的左侧已经处于limit边界之内,将该ChildView右侧所有的View回收 if (mOrientationHelper.getDecoratedStart(child) < limit || mOrientationHelper.getTransformedStartWithDecoration(child) < limit) { //[childCount - 1, i) 内部的全部要回收 recycleChildren(recycler, childCount - 1, i); return; } } } } //LinearLayoutManager.java 回收[startIndex, endIndex) private void recycleChildren(RecyclerView.Recycler recycler, int startIndex, int endIndex) { xxx for (int i = startIndex; i > endIndex; i--) { removeAndRecycleViewAt(i, recycler); } } //RecyclerView.java public void removeAndRecycleViewAt(int index, RecyclerView.Recycler recycler) { final View view = getChildAt(index); //1. 移除View removeViewAt(index); //2. 将该View回收,加入到View Pool(视图池)中,用于之后的复用。 recycler.recycleView(view); } //LinearLayoutManager.java 根据state进行布局 void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, LayoutState layoutState, LayoutChunkResult result) { //1-根据Position获得第一个View View view = layoutState.next(recycler); //2-没有更多Item需要布局了 if (view == null) { result.mFinished = true; return; } //3-addView进行添加,最终是要通过Viewgroup的addView完成添加 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); } } //4-测量 measureChildWithMargins(view, 0, 0); result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view); int left, top, right, bottom; ... //5-布局 layoutDecoratedWithMargins(view, left, top, right, bottom); ... result.mFocusable = view.hasFocusable(); } 
    原文作者:猎羽
    原文地址: https://blog.csdn.net/feather_wch/article/details/79932523
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞