android RecyclerView (三):ItemAnimator 详解

本文继上篇 ItemDecoration 之后,是深入理解 RecyclerView 系列的第二篇,关注于 ItemAnimator,主要是分析 RecyclerView Animators 这个库的原理,然后总结如何自己编写自定义的 ItemAnimator。本文涉及到的完整代码可以在 Github 获取

先看看类结构

  • DefaultItemAnimator extends SimpleItemAnimator extends RecyclerView.ItemAnimator
  • FadeInAnimator extends BaseItemAnimator extends SimpleItemAnimator extends RecyclerView.ItemAnimator
  • RecyclerView.ItemAnimator 定义了一系列 API 用于开发 item view 的动效
    • animateDisappearanceanimateAppearanceanimatePersistenceanimateChange 这4个 API 用来对 item view 进行动画显示
    • recordPreLayoutInformationrecordPostLayoutInformation 这2个 API 用来记录 item view 在 layout 前后的状态信息,这些信息封装在 ItemHolderInfo 或者其子类中,并将传递给上述4个动画API中,以便进行动画展示
    • runPendingAnimations 可以用来延迟动画到下一帧,此时就需要在上述4个 API 的实现中返回 true,并且自行记录延迟的动画信息,以便在下一帧时执行
    • dispatchAnimationStarted 和 dispatchAnimationFinished 是用来进行状态同步和事件通知的,子类必须在动画开始时调用 dispatchAnimationStarted,结束时调用 dispatchAnimationFinished,当然如果不展示动画,那就只需要直接调用 dispatchAnimationFinished
  • SimpleItemAnimator 则对 RecyclerView.ItemAnimator 的 API 进行了一次封装
    • 把父类定义的4个动画 API 转换为了 animateRemoveanimateAddanimateMoveanimateChange 这4个,为什么这样?这一次封装就把对 preLayoutInfo 和 postLayoutInfo 的处理的公共代码封装了起来,把 ItemHolderInfo 转换为了 left, top, x, y 这样的位置信息,这样,大部分动画只需要根据位置变化信息的实现,专注实现自己的动画逻辑即可,一方面复用了代码,另一方面也更好的践行了单一职责原则
    • 但是如果位置信息对于动画的展示不够,那就需要自己重写 RecyclerView.ItemAnimator的相应动画 API 了
    • 同时也定义了一系列 dispatch*** 和 on*** API,用于进行事件回调
  • DefaultItemAnimator 是 RecyclerView 包中的一个默认实现,而 BaseItemAnimator 则是 RecyclerView Animators 库中 animator 的基类,它们都继承自 SimpleItemAnimator,两者具有很大相似性,只分析后者

BaseItemAnimator

BaseItemAnimator 实现了父类的 animateRemoveanimateAddanimateMoveanimateChange 这4个 API,而实现方式都是把参数包装一下,放入相应的 animation 列表中,并返回 true,然后在 runPendingAnimations 函数中集中显示动画。为什么要这样呢?因为 recycler view 的变化是随时都可能发生的,而这样的处理就可以把动画的显示按帧对其,即两帧之间的变化,都在下一帧开始时一起处理。但是这样做有什么优势呢?暂时不得而知,DefaultItemAnimator 就是这样处理的。

例如 animateRemove 的实现如下:

@Override
public boolean animateRemove(final ViewHolder holder) { endAnimation(holder); preAnimateRemove(holder); mPendingRemovals.add(holder); return true; } 

那么下面重点看看 runPendingAnimations。

  @Override 
  public void runPendingAnimations() { boolean removalsPending = !mPendingRemovals.isEmpty(); boolean movesPending = !mPendingMoves.isEmpty(); boolean changesPending = !mPendingChanges.isEmpty(); boolean additionsPending = !mPendingAdditions.isEmpty(); if (!removalsPending && !movesPending && !additionsPending && !changesPending) { // nothing to animate return; } // First, remove stuff for (ViewHolder holder : mPendingRemovals) { doAnimateRemove(holder); } mPendingRemovals.clear(); // Next, move stuff if (movesPending) { final ArrayList<MoveInfo> moves = new ArrayList<MoveInfo>(); moves.addAll(mPendingMoves); mMovesList.add(moves); mPendingMoves.clear(); Runnable mover = new Runnable() { @Override public void run() { for (MoveInfo moveInfo : moves) { animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY, moveInfo.toX, moveInfo.toY); } moves.clear(); mMovesList.remove(moves); } }; if (removalsPending) { View view = moves.get(0).holder.itemView; ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration()); } else { mover.run(); } } // Next, change stuff, to run in parallel with move animations if (changesPending
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值