前提

我们以RecycleView 的宽高都是Match_Parent作为前提去分析,这样onMeasure方法就不会调用dispatchStep1、dispatchStep2.我们只看onLayout 方法

ViewHolder 一些属性

    public abstract static class ViewHolder {@NonNullpublic final View itemView;WeakReference<RecyclerView> mNestedRecyclerView;int mPosition = NO_POSITION;int mOldPosition = NO_POSITION;long mItemId = NO_ID;int mItemViewType = INVALID_TYPE;//之前int mPreLayoutPosition = NO_POSITION;

mPreLayoutPosition 用于dispatchLayoutStep1,表示当前ViewHolder的位置,mPosition 用于dispatchLayoutStep2 时表示ViewHolder的位置。

getLayoutPosition

 public final int getLayoutPosition() {return mPreLayoutPosition == NO_POSITION ? mPosition : mPreLayoutPosition;}

getLayoutPosition 如果当前是PreLayout阶段(dispatchLayoutStep1方法),那么该方法返回的就是mPreLayoutPosition,如果不是PreLayout阶段(dispatchLayoutStep2方法),就会返回mPosition.

首次进去RecycleView 页面

androidx.recyclerview.widget.RecyclerView#onLayout

    @Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {dispatchLayout();mFirstLayoutComplete = true;}

里面调用了dispatchLayout 方法

androidx.recyclerview.widget.RecyclerView#dispatchLayout

    void dispatchLayout() {if (mLayout == null) {Log.e(TAG, "No layout manager attached; skipping layout");// leave the state in STARTreturn;}mState.mIsMeasuring = false;if (mState.mLayoutStep == State.STEP_START) {dispatchLayoutStep1();mLayout.setExactMeasureSpecsFrom(this);dispatchLayoutStep2();} else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth()|| mLayout.getHeight() != getHeight()) {// First 2 steps are done in onMeasure but looks like we have to run again due to// changed size.mLayout.setExactMeasureSpecsFrom(this);dispatchLayoutStep2();} else {// always make sure we sync them (to ensure mode is exact)mLayout.setExactMeasureSpecsFrom(this);}dispatchLayoutStep3();}

里面layout 会分成三步。

dispatchLayoutStep1

    private void dispatchLayoutStep1() {mState.assertLayoutStep(State.STEP_START);mState.mIsMeasuring = false;//用于控制在layout期间 requestLayout不会生效startInterceptRequestLayout();//首次进去页面不会有任何处理  当我们调用notifyItemRemove 会修改oldpositionprocessAdapterUpdatesAndSetAnimationFlags();saveFocusInfo();mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged;mItemsAddedOrRemoved = mItemsChanged = false;mState.mInPreLayout = mState.mRunPredictiveAnimations;mState.mItemCount = mAdapter.getItemCount();if (mState.mRunPredictiveAnimations) {clearOldPositions();} else {//清楚OldPosition PreLayoutPositionclearOldPositions();}onExitLayoutOrScroll();stopInterceptRequestLayout(false);mState.mLayoutStep = State.STEP_LAYOUT;}

第一步主要进行ViewHolder的一些数据更新操作,比如ViewHolder 某个元素被移除,那么后面的ViewHolder的Position 都会被减一,同时如果RecycleView 有动画,那么会进行PreLayout,拿到动画位置的信息。

dispatchLayoutStep2

    private void dispatchLayoutStep2() {// Step 2: Run layoutmState.mInPreLayout = false;mLayout.onLayoutChildren(mRecycler, mState);mState.mStructureChanged = false;mPendingSavedState = null;}

真正的进行布局,如果在dispatchLayoutStep1进行过PreLayout,那么本次的布局所有的ViewHolder都是存在的,因为之前PreLayout 都已经进行了创建。
dispatchLayoutStep3

    private void dispatchLayoutStep3() {mState.assertLayoutStep(State.STEP_ANIMATIONS);onEnterLayoutOrScroll();mState.mLayoutStep = State.STEP_START;if (mState.mRunSimpleAnimations) {//执行动画}mLayout.removeAndRecycleScrapInt(mRecycler);mState.mPreviousLayoutItemCount = mState.mItemCount;}

主要进行动画的处理,回收Scrap的ViewHolder到RecycleViewPool中,比如被移除掉的ViewHolder.

调用adapter.notifyItemRemoved()方法 onLayout执行分析

注意,本篇我们以有动画的RecyclerView 进行分析,如果RecyclerView 没有动画,那么不会执行PreLayout,也就是在dispatchLayoutStep1里面并不会进行布局的填充。

第一步,更新Position

   at androidx.recyclerview.widget.RecyclerView$ViewHolder.offsetPosition(RecyclerView.java:11106)at androidx.recyclerview.widget.RecyclerView$ViewHolder.flagRemovedAndOffsetPosition(RecyclerView.java:11095)at androidx.recyclerview.widget.RecyclerView.offsetPositionRecordsForRemove(RecyclerView.java:4635)at androidx.recyclerview.widget.RecyclerView$6.offsetPositionsForRemovingLaidOutOrNewView(RecyclerView.java:996)at androidx.recyclerview.widget.AdapterHelper.postponeAndUpdateViewHolders(AdapterHelper.java:448)at androidx.recyclerview.widget.AdapterHelper.applyRemove(AdapterHelper.java:183)at androidx.recyclerview.widget.AdapterHelper.preProcess(AdapterHelper.java:102)at androidx.recyclerview.widget.RecyclerView.processAdapterUpdatesAndSetAnimationFlags(RecyclerView.java:3793)at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep1(RecyclerView.java:4039)at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:3849)at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:4404)

在dispatchLayoutStep1 方法里面,会进行更新oldposition、preLayoutPostion,preLayoutPosition 有了之后,就可以执行dispatchLayoutStep1 里面的布局.

offsetPosition() 更新oldposition、preLayoutPostion

androidx.recyclerview.widget.RecyclerView.ViewHolder#offsetPositionvoid offsetPosition(int offset, boolean applyToPreLayout) {if (mOldPosition == NO_POSITION) {mOldPosition = mPosition;}if (mPreLayoutPosition == NO_POSITION) {mPreLayoutPosition = mPosition;}if (applyToPreLayout) {mPreLayoutPosition += offset;}mPosition += offset;if (itemView.getLayoutParams() != null) {((LayoutParams) itemView.getLayoutParams()).mInsetsDirty = true;}}

比如当一个item 移除掉,如果RecycleView 里面还有多余的元素,prelayout会加载没有下一个ViewHolder.

在布局的时候,移除的view 不会被计算占用的空间

androidx.recyclerview.widget.LinearLayoutManager#layoutChunk// Consume the available space if the view is not removed OR changedif (params.isItemRemoved() || params.isItemChanged()) {result.mIgnoreConsumed = true;}
         androidx.recyclerview.widget.LinearLayoutManager#fillif (!layoutChunkResult.mIgnoreConsumed || layoutState.mScrapList != null|| !state.isPreLayout()) {layoutState.mAvailable -= layoutChunkResult.mConsumed;// we keep a separate remaining space because mAvailable is important for recyclingremainingSpace -= layoutChunkResult.mConsumed;}

当dispatchLayoutStep1 执行完毕,清除preLayoutPosition,进行dispatchLayoutStep2

androidx.recyclerview.widget.RecyclerView#dispatchLayoutStep1
androidx.recyclerview.widget.RecyclerView.ViewHolder#clearOldPosition

            // we don't process disappearing list because they may re-appear in post layout pass.clearOldPositions();

dispatchLayoutStep3 回收被移除的ViewHolder

 androidx.recyclerview.widget.RecyclerView.Recycler#recycleViewHolderInternalat androidx.recyclerview.widget.RecyclerView$Recycler.recycleViewHolderInternal(RecyclerView.java:6429)at androidx.recyclerview.widget.RecyclerView$Recycler.quickRecycleScrapView(RecyclerView.java:6554)at androidx.recyclerview.widget.RecyclerView$LayoutManager.removeAndRecycleScrapInt(RecyclerView.java:9249)at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep3(RecyclerView.java:4207)

总结:

首页进去RecycleView,只会在dispatchLayoutStep2 方法里面,对RecycleView 的布局进行填充。
如果我们调用了notifyItemRemoved 方法,在dispatchLayoutStep1 方法里面,会先更新OldPosition、PreLayoutPosition. 然后执行layout,layout之前会先调用mLayout.onLayoutChildren(mRecycler, mState); 在这个方法里面,会根据PreLayoutPosition 进行布局,因为有item 被移除,那么肯定会多出来空间,这时候,就会调用androidx.recyclerview.widget.LinearLayoutManager#fill 方法就会填充View,就会创建新的ViewHolder,在dispatchLayoutStep1 的最后,会把PreLayoutPosition 置为-1,这样就不影响dispatchLayoutStep2的布局。dispatchLayoutStep2 布局的时候,就会使用diapatchLayoutStep1创建的ViewHolder. 在dispatchStep3中,进行动画的处理,动画结束后,会把移除的ViewHolder 放到RecycleViewPool中重复利用。

RecycleView Layout 详解相关推荐

  1. Android recycleview使用详解,recycleview实现九宫格布局即横向排列,recycleview设置item占位数量大号item或小号item

    1.添加recycleview依赖 compile('com.android.support:recyclerview-v7:25.1.1') {force = true } 2.item.xml & ...

  2. View 绘制体系知识梳理(4) 绘制过程之 Layout 详解

    一.布局的起点 - performTraversals 和前面分析测量过程类似,整个布局的起点也是在ViewRootImpl的performTraversals当中: private void per ...

  3. [zz] Android五大布局Layout详解

    ref: http://blog.csdn.net/llping2011/article/details/9992941?utm_source=tuicool Remark  最常用的:LinearL ...

  4. 【转载】RecycleView使用详解

    原文链接:https://blog.csdn.net/u012721519/article/details/54692366 一.RecycleView简要介绍 RecycleView是support ...

  5. Android系统布局——android.R.layout详解

    布局文件,作为android中必不可少的一部分,android系统为了方便开发人员,在系统中定义了很多的布局文件. 系统布局文件和我们自定义的布局在写法用前缀android以示区别: 系统布局文件:a ...

  6. recycleview 使用详解,添加头部尾部,混合item,侧滑菜单,跳转到指定位置,实现九宫格布局

    添加头部尾部,混合item:https://blog.csdn.net/meixi_android/article/details/82256319 侧滑菜单:https://blog.csdn.ne ...

  7. View的绘制-layout流程详解

    目录 作用 根据 measure 测量出来的宽高,确定所有 View 的位置. 具体分析 View 本身的位置是通过它的四个点来控制的: 以下涉及到源码的部分都是版本27的,为方便理解观看,代码有所删 ...

  8. idea2019配置gradle详解_Constraint Layout 2.0 用法详解

    Constraint Layout 是最受欢迎的 Jetpack 库之一,它的 2.0 正式版本也发布啦 (目前最新版本 2.1.0-alpha1)!也许您已熟悉了 Constraint Layout ...

  9. android设置高度比例,android开发layout按比例布局(详解)

    释放双眼,带上耳机,听听看~! 由于Android的复杂性,在写程序的时候经常会遇见一些难题,也可能会遇见处理不了的问题,下面是技术狗小编详解android layout 按比例布局的代码,一起进入下 ...

最新文章

  1. 解决安卓系统写入SD卡权限问题
  2. ES6 Rest参数
  3. VS2012简单的使用感受+插件推荐
  4. python简易图形-python图形用户界面(四):教你实现一个简单实用的计时器
  5. 【Redis】配置redis主从复制
  6. c++ 宽搜(倒水)
  7. 深入理解final关键字
  8. tornado学习笔记day03-响应输出
  9. 深度学习笔记(31) 迁移与增强
  10. python编程(基于twisted的客户端编程)
  11. 伪随机数与采样(sampling)
  12. android 虚拟键 高度,Android获取虚拟按键的高度(适配全面屏)
  13. 软件测试的类型有那些?都有什么不同?
  14. 1-丁基-3-甲基咪唑醋酸盐[Bmim][Ac]|离子液体1,1,3,3,-四甲基胍乳酸盐TMGL
  15. macbook android 屏幕共享,苹果设备小技巧:iPhone,iPad,Mac进行屏幕共享和远程控制...
  16. NCBI:Refseq
  17. python调用打印机参考例子_Python调用打印机参考例子
  18. js滑动操作之-pageYOffset,scrollTop,offsetHeight,scrollHeight
  19. 好累,好好思考今后要走的路
  20. UltraEdit 快捷键操作

热门文章

  1. python实现均值滤波_python如何实现均值滤波?
  2. html研究中心,seo研究中心 教程:认识HTML、css的重要性-专业SEO技
  3. python argparse库_python标准库之argparse
  4. python requests 示例_python的requests模块实现登陆示例
  5. [译] React Native vs. Cordova、PhoneGap、Ionic,等等
  6. OpenStack Pike Minimal安装:三、镜像管理
  7. C++ cin.ignore()用法
  8. 第四次Scrum编码冲刺!!!!
  9. Web开发者用什么编辑器?
  10. ATL CLR MFC Win32 常规 的区别