☰
RecyclerView Animators 实战指南:为 Android RecyclerView 接入 ItemAnimator 与 Adapter 动画
2026/10/14 1:53:38 网站建设 项目流程
  • UI组件
  • 移动开发

【免费下载链接】recyclerview-animators

An Android Animation library which easily add itemanimator to RecyclerView items.

项目地址:https://gitcode.com/gh_mirrors/re/recyclerview-animators
点击查看免费下载

RecyclerView Animators 是一个开源的 Android 动画库,它允许开发者轻松为 RecyclerView 的 item 添加动画。本文将以其官方文档为骨架,结合仓库源码(Kotlin 版 4.0.x)深入讲解两条使用路线:基于ItemAnimator的增删移动动画,以及基于RecyclerView.Adapter包装器的入场动画。读完本文,你将掌握依赖集成、动画器选型、时长/插值器定制、AnimateViewHolder自定义动画与多重动画叠加的完整实战方案。

项目概览:两大核心能力

RecyclerView Animators 的全部能力可归结为两条主线(见 README.md 的 Features 一节):

  1. 为 item 的增删(Addition/Removal)提供ItemAnimator动画——通过替换 RecyclerView 的默认 ItemAnimator,让notifyItemInserted、notifyItemRemoved等数据更新自动带上动画效果;
  2. 为 item 的首次出现提供 Adapter 级动画——通过包装RecyclerView.Adapter,让列表内容加载时逐项播放入场动画。

两条路线互相独立、可组合使用,且从 CHANGELOG.md 可以看到,Adapter 支持(v1.1.0)、多重动画叠加(v1.2.0)、插值器定制(v1.1.3/v1.3.0)、Motion Delay(v2.2.0)等功能是随着版本逐步演进而来的。仓库自 4.0.0 起已全面 Kotlin 化,minSdkVersion提升至 21、targetSdkVersion/compileSdkVersion为 30(见 gradle.properties)。

快速集成:Gradle 依赖配置

在模块的build.gradle的dependencies部分添加依赖(Kotlin 版本声明):

dependencies { // Kotlin implementation 'jp.wasabeef:recyclerview-animators:4.0.2' }

同时确保repositories部分不仅包含"mavenCentral()",还包含指向"google()"的 maven 仓库:

repositories { google() mavenCentral() jcenter() }

从当前仓库的 animators/build.gradle 可以看到,库模块依赖androidx.recyclerview:recyclerview:1.1.0与 Kotlin 标准库,ArtifactId 为recyclerview-animators、groupId 为jp.wasabeef;工程级配置(gradle.properties)中VERSION_NAME=4.0.1、MIN_SDK_VERSION=21,且已启用 AndroidX(android.useAndroidX=true)与 Jetifier 关闭(android.enableJetifier=false),集成前请确认你的工程同样开启了 AndroidX。

方式一:基于 ItemAnimator 的增删移动动画

Step 1:为 RecyclerView 设置 ItemAnimator

拿到 RecyclerView 后,直接给itemAnimator属性赋值一个动画器即可:

val recyclerView = findViewById<RecyclerView>(R.id.list) recyclerView.itemAnimator = SlideInLeftAnimator()

所有动画器都支持通过构造函数传入插值器,例如配合回弹感更强的OvershootInterpolator:

val recyclerView = findViewById<RecyclerView>(R.id.list) recyclerView.itemAnimator = SlideInUpAnimator(OvershootInterpolator(1f))

这里1f是OvershootInterpolator的 tension(张力)参数,数值越大,越过目标位置的幅度越明显(仓库中OvershootInLeftAnimator/OvershootInRightAnimator默认张力为2.0f,见 OvershootInLeftAnimator.kt)。

Step 2:用 notifyItemXxx 触发动画

设置好 ItemAnimator 后,数据变化时请使用以下任一方法通知 RecyclerView:

  • notifyItemChanged(int)
  • notifyItemInserted(int)
  • notifyItemRemoved(int)
  • notifyItemRangeChanged(int, int)
  • notifyItemRangeInserted(int, int)
  • notifyItemRangeRemoved(int, int)

重要提示:不要依赖notifyDataSetChanged()来触发动画。这是 RecyclerView 的默认行为——该方法内部不会启动任何动画。

典型的增删操作写法:

fun remove(position: Int) { dataSet.removeAt(position) notifyItemRemoved(position) } fun add(text: String, position: Int) { dataSet.add(position, text) notifyItemInserted(position) }

仓库的示例工程 MainAdapter.kt 正是采用这一模式:remove(position)内先删除dataSet元素再调用notifyItemRemoved(position),add(text, position)内先插入数据再调用notifyItemInserted(position)。

Advanced Step 3:自定义动画时长

动画器内置了四段时长属性,可直接通过itemAnimator修改:

recyclerView.itemAnimator?.apply { addDuration = 1000 removeDuration = 100 moveDuration = 1000 changeDuration = 100 }

这四者分别控制新增、移除、移动(position 变化)与内容变更动画的时长。从源码看,BaseItemAnimator.kt 中每个具体的 Animator 在animateAddImpl/animateRemoveImpl中都会引用addDuration/removeDuration,并且还会根据 item 位置计算级联延迟:getAddDelay(holder)返回abs(holder.adapterPosition * addDuration / 4)、getRemoveDelay(holder)返回abs(holder.oldPosition * removeDuration / 4)——这意味着列表越靠后的 item 动画启动越晚,形成自然的水波式级联效果。

Advanced Step 4:更换插值器

除了构造函数传参,也可以在赋值后通过setInterpolator调整:

recyclerView.itemAnimator = SlideInLeftAnimator().apply { setInterpolator(OvershootInterpolator()) }

BaseItemAnimator的默认插值器是DecelerateInterpolator(BaseItemAnimator.kt 中protected var interpolator: Interpolator = DecelerateInterpolator()),替换为OvershootInterpolator、LinearOutSlowInInterpolator等即可获得不同的节奏感。

Advanced Step 5:实现 AnimateViewHolder 定制动画

如果希望针对不同 ViewHolder 覆盖预设动画,可以让 ViewHolder 实现AnimateViewHolder接口(定义见 AnimateViewHolder.kt),重写添加/移除动画的前置设置与播放逻辑:

class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView), AnimateViewHolder { override fun preAnimateRemoveImpl(holder: RecyclerView.ViewHolder) { // do something } override fun animateRemoveImpl(holder: RecyclerView.ViewHolder, listener: ViewPropertyAnimatorListener) { itemView.animate().apply { translationY(-itemView.height * 0.3f) alpha(0f) duration = 300 setListener(listener) }.start() } override fun preAnimateAddImpl(holder: RecyclerView.ViewHolder) { itemView.setTranslationY(-itemView.height * 0.3f) itemView.setAlpha(0f) } override fun animateAddImpl(holder: RecyclerView.ViewHolder, listener: ViewPropertyAnimatorListener) { itemView.animate().apply { translationY(0f) alpha(1f) duration = 300 setListener(listener) }.start() } }

这里接口只声明四个方法:preAnimateAddImpl/preAnimateRemoveImpl用于在动画开始前把视图置为初始状态,animateAddImpl/animateRemoveImpl用于播放具体动画并把listener挂到动画上。从源码调用链(BaseItemAnimator.kt 的doAnimateRemove/doAnimateAdd)可以确认:当holder is AnimateViewHolder时,框架会优先调用 ViewHolder 自身的实现,否则回退到各 Animator 内置的animateRemoveImpl/animateAddImpl。

动画器全览

仓库共提供 21 种现成动画器,按视觉效果分为五类:

Cool(酷炫)

LandingAnimator

LandingAnimator.kt 的入场效果是从alpha=0、scaleX/scaleY=1.5收缩淡入到正常状态,出场则是放大到 1.5 倍并淡出,模拟"着陆"的冲击感。

Scale(缩放)

ScaleInAnimator,ScaleInTopAnimator,ScaleInBottomAnimator,ScaleInLeftAnimator,ScaleInRightAnimator

基础的 ScaleInAnimator.kt 从scaleX/scaleY=0放大到 1;方向变体通过调整缩放轴心(pivot)实现,例如 ScaleInTopAnimator.kt 把pivotY设为 0(从顶部展开),ScaleInLeftAnimator.kt 把pivotX设为 0(从左侧展开)。

Fade(淡入)

FadeInAnimator,FadeInDownAnimator,FadeInUpAnimator,FadeInLeftAnimator,FadeInRightAnimator

FadeInAnimator.kt 仅做透明度动画;方向变体在淡入的同时沿对应方向位移四分之一自身高度/屏幕宽度,如 FadeInDownAnimator.kt 使用translationY = -itemView.height * 0.25f,FadeInLeftAnimator.kt 使用translationX = -rootView.width * 0.25f。

Flip(翻转)

FlipInTopXAnimator,FlipInBottomXAnimator,FlipInLeftYAnimator,FlipInRightYAnimator

绕 X 轴或 Y 轴旋转 90° 复位。以 FlipInTopXAnimator.kt 为例,入场时rotationX从 90° 回到 0°,出场则转到 90°;FlipInBottomXAnimator.kt 方向相反,从 -90° 起始。

Slide(滑入)

SlideInLeftAnimator,SlideInRightAnimator,OvershootInLeftAnimator,OvershootInRightAnimator,SlideInUpAnimator,SlideInDownAnimator

SlideInLeftAnimator.kt 入场时把translationX设为-rootView.width(从屏幕左侧外滑入);OvershootIn*系列在此基础上强制使用OvershootInterpolator(tension)获得越界回弹效果(见 OvershootInLeftAnimator.kt);SlideInUpAnimator/SlideInDownAnimator则沿垂直方向滑入并伴随透明度变化。

方式二:基于 RecyclerView.Adapter 的出现动画

如果希望列表 item 在初次加载/滚动出现时播放动画(而非数据增删),可以改用 Adapter 包装方案。

Step 1:包装 Adapter

直接以动画 Adapter 替换普通 Adapter 即可:

val recyclerView = findViewById<RecyclerView>(R.id.list) recyclerView.adapter = AlphaInAnimationAdapter(MyAdapter())

Java 写法:

RecyclerView recyclerView = findViewById(R.id.list); recyclerView.setAdapter(new AlphaInAnimationAdapter(MyAdapter());

Advanced Step 2:定制时长、插值器与首屏模式

recyclerView.adapter = AlphaInAnimationAdapter(MyAdapter()).apply { // Change the durations. setDuration(1000) // Change the interpolator. setInterpolator(vershootInterpolator()) // Disable the first scroll mode. setFirstOnly(false) }

Java 写法:

AlphaInAnimationAdapter alphaInAnimationAdapter = new AlphaInAnimationAdapter(new MyAdapter()); alphaInAnimationAdapter.setDuration(1000); alphaInAnimationAdapter.setInterpolator(new OvershootInterpolator()); alphaInAnimationAdapter.setFirstOnly(false);

这三个配置项的含义,可以从 AnimationAdapter.kt 的源码得到精确解释:

  • setDuration(int):动画时长,默认300ms;
  • setInterpolator(Interpolator):插值器,默认LinearInterpolator;
  • setFirstOnly(boolean):是否只在首次显示时播放动画,默认true。从onBindViewHolder的实现可以看出:当isFirstOnly为 true 且adapterPosition <= lastPosition(即滚动回已经出现过的位置)时,会直接调用ViewHelper.clear重置视图而不播放动画;为 false 时则每次绑定都播放。

另外还有setStartPosition(int)可设置动画从哪个位置开始记录。当出现动画播放时,框架会把duration与interpolator应用到每个ObjectAnimator上。

Advanced Step 3:多重动画叠加

动画 Adapter 本身就是普通 Adapter,因此可以互相嵌套,实现多重动画同时播放:

val alphaAdapter = AlphaInAnimationAdapter(MyAdapter()) recyclerView.adapter = ScaleInAnimationAdapter(alphaAdapter)

Java 写法:

recyclerView.setAdapter(new ScaleInAnimationAdapter(alphaInAnimationAdapter));

例如先让 item 从透明渐变出现,再叠加缩放效果。在 AnimationAdapter.kt 的getAnimators抽象方法中可以看到,每个动画 Adapter 通过返回一个Animator数组来声明自己的动画,这从机制上保证了多 Adapter 嵌套时动画可以共存。

Adapters 一览

  • Alpha:AlphaInAnimationAdapter
  • Scale:ScaleInAnimationAdapter
  • Slide:SlideInBottomAnimationAdapter、SlideInRightAnimationAdapter、SlideInLeftAnimationAdapter

从源码可以看到各实现的具体动画:

  • AlphaInAnimationAdapter.kt 通过ObjectAnimator.ofFloat(view, "alpha", from, 1f)实现透明度渐变,默认起点from=0f,构造函数第二个参数可自定义起点;
  • ScaleInAnimationAdapter.kt 同时动画scaleX与scaleY,默认起点0.5f;
  • SlideInLeftAnimationAdapter.kt 从translationX = -rootView.width滑入,SlideInRightAnimationAdapter.kt 从+rootView.width滑入,SlideInBottomAnimationAdapter.kt 从translationY = measuredHeight从底部滑入。

源码剖析:动画如何被调度执行

无论哪条路线,核心调度都在 BaseItemAnimator.kt(继承SimpleItemAnimator)中完成。它内部维护了pendingRemovals、pendingAdditions、pendingMoves、pendingChanges四个待处理队列,以及对应的四组运行中列表,runPendingAnimations()按照"先移除 → 再移动 → 变更与移动并行 → 最后添加"的顺序调度,并允许通过postOnAnimationDelayed让后置动画等待前置动画(如移除动画)完成再启动。

值得注意的两个实现细节:

  1. 属性清理:所有动画结束或取消时,都会调用 ViewHelper.kt 的clear(),把alpha、scaleX/Y、translationX/Y、rotationX/Y重置回默认值、pivot 恢复到视图中心,并清掉startDelay与插值器,避免动画残留污染后续状态;
  2. supportsChangeAnimations = false:基类在init中关闭了默认的变更动画支持,animateChange在同 ViewHolder 复用场景下回退为移动动画,从而规避常见的"item 内容变化时旧 View 卡住"问题。

示例工程 MyAnimator.kt 展示了如何用继承扩展一个自定义动画器(ScaleInRightAnimator+LinearOutSlowInInterpolator),MyAnimationAdapter.kt 则演示了如何继承AlphaInAnimationAdapter并自定义from起点——这说明两条路线都具备良好的扩展性。

综合实战:示例工程演示

仓库的 example 模块提供了两个可直接运行的演示页面:

  • AnimatorSampleActivity.kt:通过 Spinner 在 21 种BaseItemAnimator(含FadeInAnimator、LandingAnimator、ScaleInAnimator、FlipInTopXAnimator、SlideInLeftAnimator、OvershootInLeftAnimator(1.0f)等)之间动态切换recyclerView.itemAnimator,并设置了addDuration=500、removeDuration=500,同时提供"新增 item"(adapter.add(...)内部调notifyItemInserted)与"删除 item"(adapter.remove(...)内部调notifyItemRemoved)两个操作按钮;
  • AdapterSampleActivity.kt:通过 Spinner 在AlphaInAnimationAdapter、ScaleInAnimationAdapter、SlideInBottomAnimationAdapter等五种动画 Adapter 间切换,并统一调用setFirstOnly(true)、setDuration(500)、setInterpolator(OvershootInterpolator(.5f))。

两个页面都支持通过 MainActivity.kt 的开关在GridLayoutManager(context, 2)与LinearLayoutManager(context)之间切换,验证动画在列表与网格布局下的表现。

常见问题与最佳实践

  1. 动画不生效:确认使用的是notifyItemInserted/Removed等逐条通知,而非notifyDataSetChanged()——后者不会触发 ItemAnimator。
  2. 滚动时动画乱跳:Adapter 路线默认setFirstOnly(true),如需每次出现都播放请显式调用setFirstOnly(false);同时注意lastPosition只增不减,回滚到已出现位置不会重复播放。
  3. 级联延迟:getAddDelay/getRemoveDelay按位置线性放大延迟,如果列表很长,末尾 item 可能明显滞后,可通过缩短addDuration/removeDuration缓解。
  4. 自定义动画记得复用 listener:实现AnimateViewHolder时,animateAddImpl/animateRemoveImpl收到的listener(DefaultAddAnimatorListener/DefaultRemoveAnimatorListener)负责dispatchAddFinished等收尾工作,务必setListener(listener),否则动画完成后框架无法得知并可能造成状态滞留。
  5. 版本兼容:当前仓库 4.0.x 要求 minSdk 21、基于 AndroidX(recyclerview 1.1.0),老项目如未迁移 AndroidX 需评估后再引入。

结语

RecyclerView Animators 通过精心设计的BaseItemAnimator基类与AnimationAdapter包装器,把"增删移动动画"与"入场动画"两大高频需求封装成了开箱即用的组件。配合 21 种预设动画器、可配置的时长/插值器/级联延迟以及AnimateViewHolder扩展点,你可以在不改动业务 Adapter 的前提下,为列表交互注入完整、细腻的动效体验。更多细节可继续阅读 README.md、CHANGELOG.md 与animators/模块源码。

  • UI组件
  • 移动开发

【免费下载链接】recyclerview-animators

An Android Animation library which easily add itemanimator to RecyclerView items.

项目地址:https://gitcode.com/gh_mirrors/re/recyclerview-animators
点击查看免费下载
上一篇:Qwen-Image-Edit-Rapid-AIO未来展望:从图像编辑工具到完整创作平台的演进
下一篇:数据处理与存储:从文件到数据库的完整流程

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询