RecyclerView删除动画中getOldPosition()始终返回-1如何解决
问题原因
ViewHolder.getOldPosition() 仅在RecyclerView预布局(pre-layout)阶段返回有效值,当执行到runPendingAnimations时,布局更新流程已经完成,系统会自动将oldPosition重置为-1,所以这个阶段读取必然拿到无效值。而animateRemove回调恰好是在预布局阶段触发的,这时候读取的oldPosition是正确的。
解决方案
在animateRemove触发时就把对应的旧位置存下来,和ViewHolder绑定存储,不要等到runPendingAnimations再去读取。修改后参考代码如下:
class MyItemAnimator: SimpleItemAnimator() { // 存储ViewHolder和对应的旧位置,不要单独存ViewHolder private val pendingRemovals = ArrayList<Pair<RecyclerView.ViewHolder, Int>>() override fun animateRemove(holder: RecyclerView.ViewHolder): Boolean { // 预布局阶段直接读取oldPosition存起来 val oldPos = holder.oldPosition pendingRemovals.add(Pair(holder, oldPos)) // 返回true表示自定义执行动画,触发后续runPendingAnimations流程 return true } override fun runPendingAnimations(){ pendingRemovals.forEach { (holder, oldPos) -> holder.itemView.animate().apply { translationX(holder.itemView.rootView.width.toFloat()) alpha(0F) // 用提前存好的oldPos计算延时 startDelay = oldPos * 50L duration = removeDuration setListener(object : Animator.AnimatorListener { override fun onAnimationEnd(animation: Animator?) { // 动画结束必须调用dispatchRemoveFinished通知RecyclerView回收资源 dispatchRemoveFinished(holder) pendingRemovals.remove(Pair(holder, oldPos)) if (pendingRemovals.isEmpty()) { dispatchAnimationsFinished() } } override fun onAnimationStart(animation: Animator?) {} override fun onAnimationCancel(animation: Animator?) {} override fun onAnimationRepeat(animation: Animator?) {} }) }.start() } } // 剩余必须实现的基础方法按默认逻辑实现即可 override fun isRunning(): Boolean { return pendingRemovals.isNotEmpty() } override fun animateAdd(holder: RecyclerView.ViewHolder): Boolean { dispatchAddFinished(holder) return false } override fun animateMove(holder: RecyclerView.ViewHolder, fromX: Int, fromY: Int, toX: Int, toY: Int): Boolean { dispatchMoveFinished(holder) return false } override fun animateChange(oldHolder: RecyclerView.ViewHolder?, newHolder: RecyclerView.ViewHolder?, fromX: Int, fromY: Int, toX: Int, toY: Int): Boolean { if (oldHolder != newHolder && oldHolder != null) dispatchChangeFinished(oldHolder, true) if (newHolder != null) dispatchChangeFinished(newHolder, false) return false } override fun endAnimation(item: RecyclerView.ViewHolder) { item.itemView.animate().cancel() } override fun endAnimations() { pendingRemovals.forEach { it.first.itemView.animate().cancel() } pendingRemovals.clear() } }
额外注意事项
- 自定义ItemAnimator时,动画结束后必须调用对应的
dispatchXXXFinished方法通知RecyclerView回收资源,否则会出现内存泄漏、条目渲染异常等问题 - 如果你需要保留系统默认的增、改、移动动画,直接继承
DefaultItemAnimator而非SimpleItemAnimator会更省事,仅重写animateRemove和runPendingAnimations两个方法即可。
内容的提问来源于stack exchange,提问作者Some Noob Student
相关产品推荐
相关产品推荐

