You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

RecyclerView自定义动画:元素移动与添加的问题求助

RecyclerView自定义动画问题求助

问题详情

  • 严重问题:某RecyclerView使用两种不同ViewHolder布局,执行animateMove动画时,较矮元素无论上下移动都会出现定位错误,推测是measuredHeight取值异常,但无法确定具体原因。
  • 次要问题:为实现元素渐现效果设置了基于位置的启动延迟,但重新添加元素时,列表上方元素无延迟直接显示,下方元素才按位置延迟启动动画。

自定义动画代码

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.util.Log
import androidx.interpolator.view.animation.FastOutSlowInInterpolator
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.SimpleItemAnimator


// A custom animator to animate the items in recycler view
class RecyclerAnimator : SimpleItemAnimator() {

    // Never called
    override fun animateRemove(viewHolder: RecyclerView.ViewHolder): Boolean {
        viewHolder.itemView.animate()
            .alpha(0F)
            .setInterpolator(FastOutSlowInInterpolator())
            .setStartDelay(200)
            .setDuration(300)
            .scaleY(0F)
            .setListener(object : AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator) {
                    super.onAnimationEnd(animation)
                    dispatchRemoveFinished(viewHolder)
                }
            })
            .start()
        Log.d("recycler_animation", "animate remove")
        return false
    }

    // Called when the items appear in the list (launch, fragment change, it was created)
    override fun animateAdd(viewHolder: RecyclerView.ViewHolder): Boolean {
        val height: Int = viewHolder.itemView.measuredHeight / 3
        val view = viewHolder.itemView
        view.translationY = height.toFloat()
        view.alpha = 0F
        view.scaleY = 1F
        view.animate()
            .translationY(0F)
            .alpha(1F)
            .setInterpolator(FastOutSlowInInterpolator())
            .setDuration(400)
            .setStartDelay(viewHolder.bindingAdapterPosition * 50L)
            .setListener(object : AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator) {
                    super.onAnimationEnd(animation)
                    dispatchAddFinished(viewHolder)
                }
            })
            .start()
        Log.d("recycler_animation", "animate add")
        return false
    }

    // Called when an item is being moved to another position in the adapter
    override fun animateMove(
        viewHolder: RecyclerView.ViewHolder,
        fromX: Int, fromY: Int,
        toX: Int, toY: Int
    ): Boolean {
        val item = viewHolder.itemView
        item.y = fromY.toFloat()
        val verticalMovement = if (toY > fromY)
            (toY - fromY).toFloat() - (item.measuredHeight)
        else (fromY - toY).toFloat() - (item.measuredHeight)
        item.animate()
            .translationY(verticalMovement)
            .setDuration(300)
            .setInterpolator(FastOutSlowInInterpolator())
            .setStartDelay(200)
            .setListener(object : AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator) {
                    super.onAnimationEnd(animation)
                    dispatchMoveFinished(viewHolder)
                }
            })
            .start()
        Log.d("recycler_animation", "animate move")
        return false
    }

    // Called when an item changes its data
    override fun animateChange(
        oldHolder: RecyclerView.ViewHolder,
        newHolder: RecyclerView.ViewHolder,
        fromLeft: Int, fromTop: Int,
        toLeft: Int, toTop: Int
    ): Boolean {
        newHolder.itemView.alpha = 0F
        val oldAnimation = oldHolder.itemView.animate()
            .alpha(0F)
            .setInterpolator(FastOutSlowInInterpolator())
            .setDuration(300)
            .setListener(object : AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator) {
                    super.onAnimationEnd(animation)
                    dispatchChangeFinished(oldHolder, true)
                }
            })
        val newAnimation = newHolder.itemView.animate()
            .alpha(1F)
            .setInterpolator(FastOutSlowInInterpolator())
            .setDuration(300)
            .setListener(object : AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator) {
                    super.onAnimationEnd(animation)
                    dispatchChangeFinished(newHolder, false)
                }
            })
        oldAnimation.start()
        newAnimation.start()
        Log.d("recycler_animation", "animate change")
        return false
    }

    // Called when an item is deleted from the adapter
    override fun animateDisappearance(
        viewHolder: RecyclerView.ViewHolder,
        preLayoutInfo: ItemHolderInfo,
        postLayoutInfo: ItemHolderInfo?
    ): Boolean {
        viewHolder.itemView.animate()
            .alpha(0F)
            .setInterpolator(FastOutSlowInInterpolator())
            .setDuration(400)
            .setStartDelay(100)
            .scaleY(0F)
            .setListener(object : AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator) {
                    super.onAnimationEnd(animation)
                    dispatchRemoveFinished(viewHolder)
                    viewHolder.itemView.alpha = 1F
                    viewHolder.itemView.scaleY = 1F
                }
            })
            .start()
        Log.d("recycler_animation", "animate disappearance")
        return false
    }

    override fun runPendingAnimations() {
        Log.d("recycler_animation", "pending animations")
    }

    override fun endAnimation(viewHolder: RecyclerView.ViewHolder) {
        Log.d("recycler_animation", "end animation")
        val item = viewHolder.itemView
        item.alpha = 1F
        item.translationY = 0F
        item.scaleY = 1F
    }

    override fun endAnimations() {
        Log.d("recycler_animation", "end animation no arg")
    }

    override fun isRunning(): Boolean {
        return false
    }


}

问题演示

问题演示

内容的提问来源于stack exchange,提问作者m.i.n.a.r.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 17:15:14