深入讲解RecyclerView布局动画原理(二)

简介: 深入讲解RecyclerView布局动画原理(二)

3. 布局阶段


3.1 核心方法


  1. RecyclerView#dispatchLayoutStep2()
  2. LinearLayoutManager#layoutChunk()
  3. LinearLayoutManager#addDisappearingView()
  4. ViewInfoStore#addToDisappearedInLayout()


3.2 作用


  1. 根据数据源中的数据进行布局,真正展示给用户看的最终界面
  2. 如果开启动画,将被挤出屏幕的View的保存到消失动画列表中


3.3 源码解析


3.3.1 RecyclerView#dispatchLayoutStep2()


  1. 将预布局模式改为false
  2. 布局填充View

image.png

3.3.2 LinearLayoutManager#layoutChunk()


布局阶段将被挤出屏幕的View放入到DISAPPEARED动画列表中

image.png

image.png

3.3.3 LinearLayoutManager#addDisappearingView()


把Removed的View或被挤出屏幕的View添加到Disappearing动画列表

image.png

image.png


3.3.4 ViewInfoStore#addToDisappearedInLayout()


加入到Disappeared动画列表


image.png


4. 触发动画阶段



4.1 核心方法


  1. RecyclerView#dispatchLayoutStep3()
  2. ViewInfoStore#addToPostLayout()
  3. ViewInfoStore#process()
  4. ItemAnimator#animateAppearance()

4.2 作用


  1. 清理工作
  2. 保存布局后的view的信息
  3. 触发动画
  4. 动画执行完回收工作


4.3 源码解析


4.3.1 RecyclerView#dispatchLayoutStep3()


  1. 将当前屏幕上的View信息记录到postLayout动画列表中
  2. 执行动画
  3. 清理操作
  4. 布局完成回调


image.png

image.png

image.png

4.3.2 ViewInfoStore#addToPostLayout()


View信息记录到postLayout动画列表中


image.png


4.3.3 ViewInfoStore#process()


作用:执行动画


工作流程,按优先级执行


  1. 调用unuse() 将view回收掉
  2. 执行消失动画
  • 2.1 预布局中不可见调用unuse()
  • 2.2 调用processDisappeared()
  1. 调用processPersistent()执行move或者change动画
  2. 执行remove动画
  3. 执行insert动画

image.png

4.3.4 ViewInfoStore$InfoRecord


作用:定义动画类型


  • FLAG_DISAPPEARED:消失动画,包含move和remove动画
  • FLAG_APPEAR:出现动画,包含move和insert动画
  • FLAG_PRE:预布局前已经显示在RecyclerView上
  • FLAG_POST:布局后显示在RecyclerView上
  • FLAG_APPEAR_AND_DISAPPEAR:先做出现动画,再做消失动画,无意义
  • FLAG_PRE_AND_POST:预布局前和布局后一直显示在RecyclerView上
  • FLAG_APPEAR_PRE_AND_POST:在FLAG_PRE_AND_POST基础上做出现动画


image.png

4.3.5 ViewInfoStore$ProccessCallback


作用:定义四种处理动画的接口


  • processDisappeared 处理消失动画
  • processAppeared 处理出现动画
  • processPersistent 处理一直存在动画,包含move和change动画
  • unused 不需要处理动画,执行回收


image.png

4.3.6 接口实现


image.png

4.3.7 ProccessCallback#processAppeared


兵分两路


  1. 调用ItemAnimator#animateAppearance()
  2. 调用RecyclerView#postAnimationRunner()

image.png

4.3.8 一路兵:ItemAnimator#animateAppearance()


4.3.8.1 SimpleItemAnimator#animateAppearance


  1. 该方法返回true表示需要做动画
  2. 否则不需要做动画
  3. 如果预布局前View已经存在而且位置发生改变,处理MOVE动画
  4. 否则,处理ADD动画


image.png

4.3.8.2 DefaultItemAnimator.animateMove


  1. 该方法并没有真正执行动画
  2. 将MoveInfo保存到mPendingMoves中,以便RecyclerView#postAnimationRunner()使用
  3. 判断是否有必要执行MOVE动画
  4. 回到preLayout的位置


image.png

4.3.8.3 DefaultItemAnimator.animateAdd


先调用setAlpha(0),以便做淡入动画

image.png

4.3.9 二路兵:RecyclerView#postAnimationRunner()


4.3.9.1 RecyclerView#postAnimationRunner


最终调用到ItemAnimator.runPendingAnimations


image.png

4.3.9.2 DefaultItemAnimator.runPendingAnimations


  1. 首先执行Remove动画
  2. 然后同时执行Move和Change动画
  3. 最后执行Add动画


动画的总时长为removeDuration + Math.max(moveDuration, changeDuration) + addDuration

image.png

image.png

image.png

4.3.10 RecyclerView$ItemAnimatorRestoreListener


作用:动画结束后执行回收操作


  1. 动画执行完毕,removeAnimatingView
  2. 调用Recycler.recycleViewHolderInternal执行回收操作


image.png

5. 场景篇



5.1 notifyItemRemoved场景


5.1.1 场景描述


  1. 调用notifyItemRemoved()
  2. Adapter数据有100条,屏幕上有Item1~Item6 6个View,删除Item1和Item2

image.png

5.1.2 布局过程


  1. 将Item1 Item2对应的ViewHolder设置为REMOVE状态
  2. 将所有的Item对应的ViewHolder的mPreLayoutPosition字段赋值为当前的position


5.1.2.1 dispatchLayoutStep1阶段


  1. 寻找填充的锚点,寻找锚点的逻辑是,从上往下,找到第一个非remove状态的Item。在本Case中,找到Item3

image.png


  1. 移除屏幕上的Views,将它们的ViewHolder放入到Recycler的mAttachedScrap缓存中,这个缓存的好处是如果position对应上了,无需重新绑定,直接拿来用。


image.pngimage.png

image.png

从锚点Item3处往下填充,mAttachedScrap只剩下ViewHolder2和ViewHolder1

image.png

从锚点Item3处往上填充Item2 Item1,因为Item2,Imte1已经被remove掉了,它消耗的空间不会被记录,那么到步骤5的时候还可以填充

image.png


还有多余的空间,继续填充,把Item7、Item8填充到屏幕中


image.png

  1. 因为当前是预布局,直接返回

5.1.2.2 dispatchLayoutStep2阶段

  1. 寻找填充的锚点,寻找锚点的逻辑是,从上往下,找到第一个非remove状态的Item,找到Item3



image.png

  1. 移除屏幕上的Views,将它们的ViewHolder放入到Recycler的mAttachedScrap缓存中


image.png

从锚点Item3处往下填充,填充到Item6为止,就没有足够的距离了,mAttachedScrap只剩下ViewHolder8,ViewHolder7,ViewHolder2,ViewHolder1

image.png

往上填充,虽然此时还有两个View的高度,但是此时,上边没有数据了,此处不填充

image.png

此时还有两个View的高度,继续往下填充

image.png

修复GAP

image.png

  1. 当前是布局阶段,但是因为ViewHolder1和ViewHolder2都是被Remove掉的,所以跳过


image.png

5.1.2.3 dispatchLayoutStep3阶段

  1. Item1、Item2做消失动画
  2. Item3、Item4~Item8做移动动画
  3. 动画结束后,Item1、Item2会被回收到mCachedViews缓存池中

image.png

5.2 notifyItemInserted场景


5.2.1 场景描述


假设在Item1下面插入两条数据AddItem1,AddItem2


image.png

5.2.2 布局过程


5.2.2.1 dispatchLayoutStep1阶段


  1. 寻找锚点,找到Item1

image.png

2. 移除屏幕上的Views,放入到mAttachedScrap中

image.png

3. 锚点处从上往下填充

image.png

4. 锚点处从下往上填充,由上图可知,上面没有空间了,不填充 5. 判断是否还有剩余的空间,如果有在末尾填充,下面没空间了,不填充 6. 因为当前是预布局阶段,不填充


5.2.2.2 dispatchLayoutStep2阶段


  1. 寻找锚点,找到Item1

image.png

2. 移除屏幕上的Views,放入到mAttachedScrap中

image.png

3. 锚点处从上往下填充,此时将变化后的数据填充到屏幕上,addItem1和addItem2被填充到item1下面

image.png

4. 锚点处从下往上填充,由图可知,没有空间不填充 5. 判断是否还有剩余的空间,由图可知,没有空间不填充 6. 当前是layoutStep2阶段,会将mAttachScrap的内容,填充到屏幕末尾,ViewHolder5和ViewHolder6对应的ItemView被填充


image.png


5.2.2.3 dispatchLayoutStep3阶段


  1. Item2、Item3~Item6做移动动画
  2. addItem1、addItem2做淡入动画
  3. 动画结束后Item5、Item6被回收到mCachedViews缓存池中

image.png

5.3 场景总结


5.3.1 notifyItemRemoved场景

image.png

5.3.2 notifyItemInserted场景

image.png


相关文章
|
Android开发 开发者
RecyclerView定制:通用ItemDecoration及全展开RecyclerView的实现
RecyclerView定制:通用ItemDecoration及全展开RecyclerView的实现
448 0
RecyclerView定制:通用ItemDecoration及全展开RecyclerView的实现
RecyclerView#smoothScrollToPosition调用RecyclerView#OnScrollListener的过程
项目中使用到了RecyclerView#smoothScrollToPosition(0)方法让Recyclerview滚动到顶部,同时给Recyclerview设置了监听器RecyclerView.OnScrollListener。
RecyclerView学习-RecyclerView#Adapter#notifyDataSetChanged是如何更新数据的?
RecyclerView学习-RecyclerView#Adapter#notifyDataSetChanged是如何更新数据的?
|
缓存 算法
深入讲解RecyclerView布局动画原理(一)
深入讲解RecyclerView布局动画原理(一)
深入讲解RecyclerView布局动画原理(一)
|
缓存 Android开发
Android RecyclerView 绘制流程及Recycler缓存(上)
RecyclerView 源码一万多行,想全部读懂学会挺麻烦的,感兴趣的可以自己去瞅瞅,这篇文章重点来看下 RecyclerView是如何一步步将每一个 ItemView 显示到屏幕上,然后再分析在显示和滑动过程中,是如何通过缓存复用来提升整体性能的。 RecyclerView本质上也是一个 自定义控件 ,因此我们可以沿着分析其 onMeasure -> onLayout -> onDraw 这 3 个方法的路线来深入研究。
237 0
Android RecyclerView 绘制流程及Recycler缓存(上)
|
缓存 算法
面试官:RecyclerView布局动画原理了解吗?
面试官:RecyclerView布局动画原理了解吗?
面试官:RecyclerView布局动画原理了解吗?
SwipeRefreshLayout 嵌套 RecyclerView滑动冲突
SwipeRefreshLayout 嵌套 RecyclerView滑动冲突
323 0
|
缓存 Android开发 容器
Android RecyclerView 绘制流程及Recycler缓存(下)
缓存复用原理 Recycler 缓存复用是 RecyclerView 中另一个非常重要的机制,这套机制主要实现了 ViewHolder 的缓存以及复用。
412 0
|
Java 容器
【RecyclerView】二、RecyclerView 简介 ( RecyclerView 特点 | RecyclerView 涉及到的类 )
【RecyclerView】二、RecyclerView 简介 ( RecyclerView 特点 | RecyclerView 涉及到的类 )
190 0
|
Android开发
【Android 事件分发】ItemTouchHelper 事件分发源码分析 ( 绑定 RecyclerView )(一)
【Android 事件分发】ItemTouchHelper 事件分发源码分析 ( 绑定 RecyclerView )(一)
199 0