解析RecyclerView中itemView的动画

 上篇文章主要讲的是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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值