解析RecyclerView中itemView的动画

来源:互联网 发布:Sql 去冗 group by 编辑:程序博客网 时间:2024/04/29 06:31

 上篇文章主要讲的是itemView的绘制流程。

  分析完上篇文章后,我又开始疑惑了,itemView的动画是如何绘制的呢 ? 

  从源码来分析这个问题,从上篇文章我们看到,RecyclerView 绘制的关键是onLayout 里面的position 位置确定,因此直接上源码:

     

 @Override    protected void onLayout(boolean changed , int l , int t , int r , int b) {        eatRequestLayout();        dispatchLayout();        resumeRequestLayout(false);        mFirstLayoutComplete = true;    }
 里面关键的是: 

  dispatchLayout()

  

void dispatchLayout() {        if (mAdapter == null) {            Log.e(TAG, "No adapter attached; skipping layout");            return;        }        eatRequestLayout();        // simple animations are a subset of advanced animations (which will cause a        // prelayout step)        boolean animateChangesSimple = mItemAnimator != null && mItemsAddedOrRemoved && !mItemsChanged;        final boolean animateChangesAdvanced = ENABLE_PREDICTIVE_ANIMATIONS && animateChangesSimple && predictiveItemAnimationsEnabled();        mItemsAddedOrRemoved = mItemsChanged = false;        ArrayMap<View, Rect> appearingViewInitialBounds = null;        mState.mInPreLayout = animateChangesAdvanced;        mState.mItemCount = mAdapter.getItemCount();        if (animateChangesSimple) {            // Step 0: Find out where all non-removed items are, pre-layout            mState.mPreLayoutHolderMap.clear();            mState.mPostLayoutHolderMap.clear();            final int count = getChildCount();            for (int i = 0; i < count; ++i) {                final ViewHolder holder = getChildViewHolderInt(getChildAt(i));                final View view = holder.itemView;                mState.mPreLayoutHolderMap.put(holder, new ItemHolderInfo(holder, view.getLeft(), view.getTop(), view.getRight(), view.getBottom(), holder.mPosition));            }        }        if (animateChangesAdvanced) {            // Step 1: run prelayout: This will use the old positions of items. The layout manager            // is expected to layout everything, even removed items (though not to add removed            // items back to the container). This gives the pre-layout position of APPEARING views            // which come into existence as part of the real layout.            mInPreLayout = true;            final boolean didStructureChange = mState.mStructureChanged;            mState.mStructureChanged = false;            // temporarily disable flag because we are asking for previous layout            mLayout.onLayoutChildren(mRecycler, mState);            mState.mStructureChanged = didStructureChange;            mInPreLayout = false;            appearingViewInitialBounds = new ArrayMap<View, Rect>();            for (int i = 0; i < getChildCount(); ++i) {                boolean found = false;                final View child = getChildAt(i);                for (int j = 0; j < mState.mPreLayoutHolderMap.size(); ++j) {                    final ViewHolder holder = mState.mPreLayoutHolderMap.keyAt(j);                    if (holder.itemView == child) {                        found = true;                        continue;                    }                }                if (!found) {                    appearingViewInitialBounds.put(child, new Rect(child.getLeft(), child.getTop(), child.getRight(), child.getBottom()));                }            }        }        clearOldPositions();        dispatchLayoutUpdates();        mState.mItemCount = mAdapter.getItemCount();        // Step 2: Run layout        mState.mInPreLayout = false;        mLayout.onLayoutChildren(mRecycler, mState);        mState.mStructureChanged = false;        mPendingSavedState = null;        // onLayoutChildren may have caused client code to disable item animations; re-check        animateChangesSimple = animateChangesSimple && mItemAnimator != null;        if (animateChangesSimple) {            // Step 3: Find out where things are now, post-layout            int count = getChildCount();            for (int i = 0; i < count; ++i) {                final ViewHolder holder = getChildViewHolderInt(getChildAt(i));                final View view = holder.itemView;                mState.mPostLayoutHolderMap.put(holder, new ItemHolderInfo(holder, view.getLeft(), view.getTop(), view.getRight(), view.getBottom(), holder.mPosition));            }            // Step 4: Animate DISAPPEARING and REMOVED items            final int preLayoutCount = mState.mPreLayoutHolderMap.size();            for (int i = preLayoutCount - 1; i >= 0; i--) {                final ViewHolder itemHolder = mState.mPreLayoutHolderMap.keyAt(i);                if (!mState.mPostLayoutHolderMap.containsKey(itemHolder)) {                    final ItemHolderInfo disappearingItem = mState.mPreLayoutHolderMap.valueAt(i);                    mState.mPreLayoutHolderMap.removeAt(i);                    final View disappearingItemView = disappearingItem.holder.itemView;                    removeDetachedView(disappearingItemView, false);                    mRecycler.unscrapView(disappearingItem.holder);                    animateDisappearance(disappearingItem);                }            }            // Step 5: Animate APPEARING and ADDED items            final int postLayoutCount = mState.mPostLayoutHolderMap.size();            if (postLayoutCount > 0) {                for (int i = postLayoutCount - 1; i >= 0; i--) {                    final ViewHolder itemHolder = mState.mPostLayoutHolderMap.keyAt(i);                    final ItemHolderInfo info = mState.mPostLayoutHolderMap.valueAt(i);                    if ((mState.mPreLayoutHolderMap.isEmpty() || !mState.mPreLayoutHolderMap.containsKey(itemHolder))) {                        mState.mPostLayoutHolderMap.removeAt(i);                        final Rect initialBounds = (appearingViewInitialBounds != null) ? appearingViewInitialBounds.get(itemHolder.itemView) : null;                        animateAppearance(itemHolder, initialBounds, info.left, info.top);                    }                }            }            // Step 6: Animate PERSISTENT items            count = mState.mPostLayoutHolderMap.size();            for (int i = 0; i < count; ++i) {                final ViewHolder postHolder = mState.mPostLayoutHolderMap.keyAt(i);                final ItemHolderInfo postInfo = mState.mPostLayoutHolderMap.valueAt(i);                final ItemHolderInfo preInfo = mState.mPreLayoutHolderMap.get(postHolder);                if (preInfo != null && postInfo != null) {                    if (preInfo.left != postInfo.left || preInfo.top != postInfo.top) {                        postHolder.setIsRecyclable(false);                        if (DEBUG) {                            Log.d(TAG, "PERSISTENT: " + postHolder + " with view " + postHolder.itemView);                        }                        if (mItemAnimator.animateMove(postHolder, preInfo.left, preInfo.top, postInfo.left, postInfo.top)) {                            postAnimationRunner();                        }                    }                }            }        }        resumeRequestLayout(false);        mLayout.removeAndRecycleScrapInt(mRecycler, !animateChangesAdvanced);        mState.mPreviousLayoutItemCount = mState.mItemCount;        mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;    }
  从开始进行分析,boolean animateChangesSimple = mItemAnimator != null && mItemsAddedOrRemoved && !mItemsChanged;   mItemAnimator 这个参数不为空,mItemsAddedOrRemoved  表示是添加和删除了itemView追踪一下,发现他在

 

 void updateChildViews() {        final int opCount = mPendingUpdates.size();        for (int i = 0; i < opCount; i++) {            final UpdateOp op = mPendingUpdates.get(i);            switch (op.cmd) {                case UpdateOp.ADD:                    if (DEBUG) {                        Log.d(TAG, "UpdateOp.ADD start=" + op.positionStart + " count=" + op.itemCount);                    }                    offsetPositionRecordsForInsert(op.positionStart, op.itemCount);                    mItemsAddedOrRemoved = true;                    break;                case UpdateOp.REMOVE:                    if (DEBUG) {                        Log.d(TAG, "UpdateOp.REMOVE start=" + op.positionStart + " count=" + op.itemCount);                    }                    for (int j = 0; j < op.itemCount; ++j) {                        final ViewHolder holder = findViewHolderForPosition(op.positionStart + j, true);                        if (holder != null) {                            holder.setIsRecyclable(false);                        }                        else {                            mState.mDeletedInvisibleItemCountSincePreviousLayout++;                        }                    }                    offsetPositionRecordsForRemove(op.positionStart, op.itemCount);                    mItemsAddedOrRemoved = true;                    break;                case UpdateOp.UPDATE:                    if (DEBUG) {                        Log.d(TAG, "UpdateOp.UPDATE start=" + op.positionStart + " count=" + op.itemCount);                    }                    viewRangeUpdate(op.positionStart, op.itemCount);                    mItemsChanged = true;                    break;            }            mPendingLayoutUpdates.add(op);            // TODO: recycle the op if no animator (also don't bother stashing in pending layout updates?)        }        mPendingUpdates.clear();    }
 发现在这个里面赋值为true,很明显的是UpdateOp操作为 ADD REMOVEDE 的时候,赋值为true,这里涉及到观察者模式,下篇文章会具体讲这个问题,在这里我们知道是我们手动通知了adapter 数据改变了。接着上面的讲,animateChangesSimple 为true,下面的变量animateChangesAdvanced 为true,mItemsAddedOrRemoved = mItemsChanged = false 避免下次重复执行动画的处理。


  if (animateChangesSimple) {            // Step 0: Find out where all non-removed items are, pre-layout            mState.mPreLayoutHolderMap.clear();            mState.mPostLayoutHolderMap.clear();            final int count = getChildCount();            for (int i = 0; i < count; ++i) {                final ViewHolder holder = getChildViewHolderInt(getChildAt(i));                final View view = holder.itemView;                mState.mPreLayoutHolderMap.put(holder, new ItemHolderInfo(holder, view.getLeft(), view.getTop(), view.getRight(), view.getBottom(), holder.mPosition));            }        }

 因为上面我们已经确定animateChangesSimple 为true,注意: 这段 代码就是把所有的itemView的现状保存一下。

   if (animateChangesAdvanced) {            // Step 1: run prelayout: This will use the old positions of items. The layout manager            // is expected to layout everything, even removed items (though not to add removed            // items back to the container). This gives the pre-layout position of APPEARING views            // which come into existence as part of the real layout.            mInPreLayout = true;            final boolean didStructureChange = mState.mStructureChanged;            mState.mStructureChanged = false;            // temporarily disable flag because we are asking for previous layout            mLayout.onLayoutChildren(mRecycler, mState);            mState.mStructureChanged = didStructureChange;            mInPreLayout = false;            appearingViewInitialBounds = new ArrayMap<View, Rect>();            for (int i = 0; i < getChildCount(); ++i) {                boolean found = false;                final View child = getChildAt(i);                for (int j = 0; j < mState.mPreLayoutHolderMap.size(); ++j) {                    final ViewHolder holder = mState.mPreLayoutHolderMap.keyAt(j);                    if (holder.itemView == child) {                        found = true;                        continue;                    }                }                if (!found) {                    appearingViewInitialBounds.put(child, new Rect(child.getLeft(), child.getTop(), child.getRight(), child.getBottom()));                }            }        }
  里面有一段关键代码, 
 
mLayout.onLayoutChildren(mRecycler, mState);
这个主要是重新布局,后面的for循环是为了获取新增加的itemView,存储在了 appearingViewInitialBounds这个集合中

clearOldPositions()是清除itemView的位置信息;

  mState.mInPreLayout = false;        mLayout.onLayoutChildren(mRecycler, mState);

主要是调用LayoutManager 中的onlayoutChidren 方法,来为itemview 确定位置;

下面重点来了,

 

 // Step 3: Find out where things are now, post-layout            int count = getChildCount();            for (int i = 0; i < count; ++i) {                final ViewHolder holder = getChildViewHolderInt(getChildAt(i));                final View view = holder.itemView;                mState.mPostLayoutHolderMap.put(holder, new ItemHolderInfo(holder, view.getLeft(), view.getTop(), view.getRight(), view.getBottom(), holder.mPosition));            }
保存itemView的最终位置 

下面就开始执行“删除itemView ” 时候的动画了 

  

// Step 4: Animate DISAPPEARING and REMOVED items            final int preLayoutCount = mState.mPreLayoutHolderMap.size();            for (int i = preLayoutCount - 1; i >= 0; i--) {                final ViewHolder itemHolder = mState.mPreLayoutHolderMap.keyAt(i);                if (!mState.mPostLayoutHolderMap.containsKey(itemHolder)) {                    final ItemHolderInfo disappearingItem = mState.mPreLayoutHolderMap.valueAt(i);                    mState.mPreLayoutHolderMap.removeAt(i);                    final View disappearingItemView = disappearingItem.holder.itemView;                    removeDetachedView(disappearingItemView, false);                    mRecycler.unscrapView(disappearingItem.holder);                    animateDisappearance(disappearingItem);                }            }
   上下的是执行那些新增加的 ItemView的动画

 

// Step 5: Animate APPEARING and ADDED items            final int postLayoutCount = mState.mPostLayoutHolderMap.size();            if (postLayoutCount > 0) {                for (int i = postLayoutCount - 1; i >= 0; i--) {                    final ViewHolder itemHolder = mState.mPostLayoutHolderMap.keyAt(i);                    final ItemHolderInfo info = mState.mPostLayoutHolderMap.valueAt(i);                    if ((mState.mPreLayoutHolderMap.isEmpty() || !mState.mPreLayoutHolderMap.containsKey(itemHolder))) {                        mState.mPostLayoutHolderMap.removeAt(i);                        final Rect initialBounds = (appearingViewInitialBounds != null) ? appearingViewInitialBounds.get(itemHolder.itemView) : null;                        animateAppearance(itemHolder, initialBounds, info.left, info.top);                    }                }            }
 下面执行的剩余itemView的动画(位置有可能发生了变化)

  // Step 6: Animate PERSISTENT items            count = mState.mPostLayoutHolderMap.size();            for (int i = 0; i < count; ++i) {                final ViewHolder postHolder = mState.mPostLayoutHolderMap.keyAt(i);                final ItemHolderInfo postInfo = mState.mPostLayoutHolderMap.valueAt(i);                final ItemHolderInfo preInfo = mState.mPreLayoutHolderMap.get(postHolder);                if (preInfo != null && postInfo != null) {                    if (preInfo.left != postInfo.left || preInfo.top != postInfo.top) {                        postHolder.setIsRecyclable(false);                        if (DEBUG) {                            Log.d(TAG, "PERSISTENT: " + postHolder + " with view " + postHolder.itemView);                        }                        if (mItemAnimator.animateMove(postHolder, preInfo.left, preInfo.top, postInfo.left, postInfo.top)) {                            postAnimationRunner();                        }                    }                }            }

   至于每个itemView的动画交给了  DefaultItemAnimator 这个类进行处理,不停的更新的动画itemView的位置或者是透明度以及其他的数据。

   主要的逻辑就是:

       增加/删除itemView -  保留itemView 的位置 -进行预处理(测量所有itemView的位置),获取新增的itemView-   清除所有itemView的位置信息-  测量以及定位所有的itemView- 保存itemview的最终动画的位置以及其他信息- 处理删除remove的itemView- 处理被add的itemview的动画-处理剩余itemview 位置改变的itemview的动画。

   上面就是动画绘制的主要过程。

 



 
 
1 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 绝地求生打不准怎么办 身份证号码变更后社保怎么办 社保与身份不符怎么办 年龄改了学籍怎么办 结婚证信息错误怎么办 六级身份证过期怎么办 身份号泄露了怎么办 身体证信息泄露怎么办 手机号被泄漏了怎么办 姓名和电话泄露怎么办 个人身份证信息泄露怎么办 身份号码泄露了怎么办 我身份证泄露了怎么办 身份证信息泄漏了怎么办 无锡身份证丢了怎么办 人在外地怎么办身份证 欠空放公司不还怎么办 兼职要身份证照片怎么办 身份证刷不了磁怎么办 身份证不能刷了怎么办 身份证指纹错了怎么办 指纹手机丢了怎么办 异地办理临时身份证怎么办 杭州办理外地身份证怎么办 办理身份证没有户口本怎么办 2018身份证掉了怎么办 双户口注销社保怎么办 常用户口被注销怎么办 刚到厦门怎么办身份证 新疆身份证丢了怎么办 技能证书丢了怎么办 16岁拍身份证怎么办 16岁以下怎么办身份证 办身份证要证明怎么办 15岁怎么办银行卡淘宝 满16岁怎么办银行卡 电脑最小化后不见了怎么办 满16周岁怎么办银行卡 给儿童办身份证怎么办 当兵前身份证号码不符怎么办 身份证后六位密码x怎么办