解析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
- 解析RecyclerView中itemView的动画
- RecyclerView.java源码动画分析,每个itemView的动画交给了 DefaultItemAnimator 这个类进行处理
- RecyclerView的itemView的点击效果
- ListView、recyclerView的itemView布局注意事项
- 用RecyclerView轻松实现gridview中itemview拖拽效果
- Android RecyclerView 加载更多数据 及 不同类型itemView的使用
- ListView 中ItemView 的重用原理
- Android中RecyclerView的使用与解析
- 解决RecyclerView加载图片时复用itemview导致的图片数量错乱问题
- Kotlin在ViewHoler定义变量注意不要跟RecyclerView里面的ViewHolder中的itemView冲突
- Listview的itemview中包含edittext控件时的解决方案
- ListView 中添加按钮,动态删除添加ItemView的操作
- ListView 中添加按钮,动态删除添加ItemView的操作
- RecycleView获取当前屏幕中itemview的显示区域
- 万能的ItemView
- Android中RecyclerView的item实现动画的效果
- RecyclerView的使用解析
- recyclerview item的进入动画
- (11.1.4)Hadoop基础教程之分布式环境搭建
- resizableImageWithCapInsets 使用须知
- Remove Element
- 几个常用的API HOOK的工具包
- (面经一)本校临时面“亚信联创”
- 解析RecyclerView中itemView的动画
- 【KMP】【POJ3461】Oulipo
- 兔子--android项目结构
- over partition by 函数
- (11.1.5)Hadoop基础教程之搭建开发环境及编写Hello World
- Android快速开发系列 10个常用工具类
- JTA 深度历险 - 原理与实现
- HDOJ Keep on Truckin' 1037
- webdriver中元素查找常用方法