05 RecyclerView-布局过程

作者: 凤邪摩羯 | 来源:发表于2021-07-20 09:16 被阅读0次

    前言

    本文中,我将从RecyclerView插入Item 和 删除Item的两个场景,通过dispatchLayoutStep1、dispatchLayoutStep2、dispatchLayoutStep3各个阶段下来说明RecyclerView的布局情况。


    场景1:插入Item

    1.1 场景说明

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

    1.2 核心方法

    notifyItemInserted()

    1.3 各个阶段布局情况

    阶段1:dispatchLayoutStep1()

    1. 寻找锚点,找到Item1 图片
    2. 移除屏幕上的Views,放入到mAttachedScrap中 图片
    3. 锚点处从上往下填充 图片
    4. 锚点处从下往上填充,由上图可知,上面没有空间了,不填充

    5. 判断是否还有剩余的空间,如果有在末尾填充,下面没空间了,不填充

    6. 因为当前是预布局阶段,不填充

    阶段2:dispatchLayoutStep2()

    1. 寻找锚点,找到Item1 图片
    2. 移除屏幕上的Views,放入到mAttachedScrap中 图片
    3. 锚点处从上往下填充,此时将变化后的数据填充到屏幕上,addItem1和addItem2被填充到item1下面 图片
    4. 锚点处从下往上填充,由图可知,没有空间不填充

    5. 判断是否还有剩余的空间,由图可知,没有空间不填充

    6. 当前是layoutStep2阶段,会将mAttachScrap的内容,填充到屏幕末尾,ViewHolder5和ViewHolder6对应的ItemView被填充

    图片

    阶段3:dispatchLayoutStep3阶段

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

    1.4 总结

    在Item1下面增加两个Item场景,各个layout阶段的布局情况

    图片

    场景2:删除Item

    2.1 场景说明

    屏幕上有Item1-Item6,共6个View,目前需删除Item1和Item2。 图片

    2.2 核心方法

    notifyItemRemoved()

    2.3 各个阶段布局情况

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

    我们回顾以下onLayoutChildren的几个步骤

    1. 寻找填充的锚点(最终调用findReferenceChild方法)
    2. 移除屏幕上的Views(最终调用detachAndScrapAttachedViews方法)
    3. 从锚点处从上往下填充(调用fill和layoutChunk方法)
    4. 从锚点处从下往上填充(调用fill和layoutChunk方法)
    5. 如果还有多余的空间,继续填充(调用fill和layoutChunk方法)
    6. 非预布局,将scrapList中多余的ViewHolder填充(调用layoutForPredictiveAnimations)

    阶段1:dispatchLayoutStep1()

    1. 寻找填充的锚点,寻找锚点的逻辑是,从上往下,找到第一个非remove状态的Item。在本Case中,找到Item3 图片
    2. 移除屏幕上的Views,将它们的ViewHolder放入到Recycler的mAttachedScrap缓存中,这个缓存的好处是如果position对应上了,无需重新绑定,直接拿来用。 图片
    3. 从锚点Item3处往下填充,mAttachedScrap只剩下ViewHolder2和ViewHolder1 图片
    4. 从锚点Item3处往上填充Item2 Item1,因为Item2,Imte1已经被remove掉了,它消耗的空间不会被记录,那么到步骤5的时候还可以填充 图片
    5. 还有多余的空间,继续填充,把Item7、Item8填充到屏幕中 图片
    6. 因为当前是预布局,直接返回

    至此step1的layout结束

    阶段2:dispatchLayoutStep2阶段

    1. 寻找填充的锚点,寻找锚点的逻辑是,从上往下,找到第一个非remove状态的Item。在本Case中,找到Item3 图片
    2. 移除屏幕上的Views,将它们的ViewHolder放入到Recycler的mAttachedScrap缓存中 图片
    3. 从锚点Item3处往下填充,填充到Item6为止,就没有足够的距离了,mAttachedScrap只剩下ViewHolder8,ViewHolder7,ViewHolder2,ViewHolder1 图片
    4. 往上填充,虽然此时还有两个View的高度,但是此时,上边没有数据了,此处不填充 图片
    5. 此时还有两个View的高度,继续往下填充 图片

    「注意此时已经布局完成但是屏幕上部与第一个有GAP,会修复」

     if (getChildCount() > 0) {            // because layout from end may be changed by scroll to position            // we re-calculate it.            // find which side we should check for gaps.            if (mShouldReverseLayout ^ mStackFromEnd) {                int fixOffset = fixLayoutEndGap(endOffset, recycler, state, true);                startOffset += fixOffset;                endOffset += fixOffset;                fixOffset = fixLayoutStartGap(startOffset, recycler, state, false);                startOffset += fixOffset;                endOffset += fixOffset;            } else {                int fixOffset = fixLayoutStartGap(startOffset, recycler, state, true);                startOffset += fixOffset;                endOffset += fixOffset;                fixOffset = fixLayoutEndGap(endOffset, recycler, state, false);                startOffset += fixOffset;                endOffset += fixOffset;            }        }
    
    修复后效果如下 图片
    1. 当前不是预布局,但是因为ViewHolder1和ViewHolder2都是被Remove掉的,所以跳过 图片

    阶段3:dispatchLayoutStep3阶段

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

    2.4 总结

    图片

    至此,关于RecyclerView布局过程讲解完毕。

    相关文章

      网友评论

        本文标题:05 RecyclerView-布局过程

        本文链接:https://www.haomeiwen.com/subject/ngijfltx.html