关闭 RecyclerView 子尺寸变化动画

Turn off RecyclerView child size change animation

我有一个视图,我想在点击时更改它的大小。

我有以下测试布局:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <pro.labster.coloringbook.ui.view.ColorView
        android:id="@+id/colorView"
        android:layout_width="@dimen/colorSize"
        android:layout_height="@dimen/colorSize"
        android:layout_centerInParent="true"
        app:normalSize="@dimen/colorSize"
        app:selectedSize="@dimen/selectedColorSize" />

</RelativeLayout>

以及以下代码:

val colorView = findViewById<ColorView>(R.id.colorView)
colorView.setBackgroundColor(Color.RED)
colorView.setOnClickListener {
    isSelected = !isSelected
    colorView.setColorSelected(isSelected)
}

尺码变更代码:

fun setColorSelected(isSelected: Boolean) {
        if (isColorSelected != isSelected) {
            if (isSelected) {
                setCurrentSize(selectedSize.toInt())
            } else {
                setCurrentSize(normalSize.toInt())
            }
        }

        isColorSelected = isSelected
    }

private fun setCurrentSize(size: Int) {
    if (layoutParams.height != size || layoutParams.width != size) {
        layoutParams.width = size
        layoutParams.height = size
        requestLayout()
    }
}

效果不错:

https://www.youtube.com/watch?v=Ft8xcX5Qxbg

但是如果我将此视图添加到 RecyclerView,它会滞后于大小更改:

class ColorsAdapter(colorsHex: List<String>) : RecyclerView.Adapter<ColorsAdapter.ViewHolder>() {

    private val colors = mutableListOf<Int>()
    private var selectedPosition: Int = 0

    init {
        colorsHex.forEach {
            colors.add(Color.parseColor(it))
        }
    }

    override fun getItemCount() = colors.size

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val color = colors[position]

        holder.colorView.setBackgroundColor(color)
        holder.colorView.tag = position

        holder.colorView.setColorSelected(position == selectedPosition)
    }

    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder {
        val inflater = LayoutInflater.from(parent?.context)

        val view = inflater.inflate(R.layout.view_item_color, parent, false)

        return ViewHolder(view)
    }


    inner class ViewHolder(itemView: View?) : RecyclerView.ViewHolder(itemView) {
        val colorView: ColorView = itemView!!.findViewById(R.id.colorView)

        init {
            colorView.setOnClickListener {
                val oldPosition = selectedPosition

                selectedPosition = colorView.tag as Int

                if (oldPosition != selectedPosition) {
                    notifyItemChanged(oldPosition)
                    notifyItemChanged(selectedPosition)
                }
            }
        }
    }

}

view_item_color.xml:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="@dimen/selectedColorSize"
    android:layout_height="@dimen/selectedColorSize">

    <pro.labster.coloringbook.ui.view.ColorView
        android:id="@+id/colorView"
        android:layout_width="@dimen/colorSize"
        android:layout_height="@dimen/colorSize"
        android:layout_gravity="center"
        app:normalSize="@dimen/colorSize"
        app:selectedSize="@dimen/selectedColorSize" />

</FrameLayout>

https://www.youtube.com/watch?v=m8g6zpj9aDg

如我所见,它还尝试对尺寸变化进行动画处理——是这样吗?

如何解决这个延迟?

docs of DefaultItemAnimator 中所示:

This implementation of RecyclerView.ItemAnimator provides basic animations on remove, add, and move events that happen to the items in a RecyclerView. RecyclerView uses a DefaultItemAnimator by default.

如果要删除这些动画,则取消默认动画制作器:

recyclerview.itemAnimator = null

您可以通过 Item Animator 完成此操作并初始化它们

您正在使用 notifyItemChanged(position: Int),RecyclerView 不知道到底发生了什么变化 - 可能项目被替换为另一个项目,所以新的 ViewHolder 被绑定到适配器 onBindViewHolder(holder: ViewHolder, position:Int) 并替换旧的使用默认动画。

如果您明确知道更改的内容,您可以使用 notifyItemChanged(position: Int, payload: Any) 为适配器提供更改负载,然后 RecyclerView 将仅对适配器中的 ViewHolder 执行部分更新 onBindViewHolder(holder: ViewHolder, position:Int, payloads: MutableList<Any>) 而不会替换它。

例如,您可以在 ViewHolder 中更改:

init {
    colorView.setOnClickListener {
    val oldPosition = selectedPosition

    selectedPosition = colorView.tag as Int

    if (oldPosition != selectedPosition) {
       notifyItemChanged(oldPosition, false)   // include payloads
       notifyItemChanged(selectedPosition, true)
       }
    }
}

然后在您的适配器中覆盖:

override fun onBindViewHolder(holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
    if(payloads.size < 1){
        //if there are no payloads perform full binding
        onBindViewHolder(holder, position)
        return
    }
    // if viewHolder can consume incremental updates iterate over payloads
    // otherwise it's enough to grab the last update
    // cast as Boolean is safe because only Booleans are passed as payloads
    holder.colorView.setColorSelected(payloads.last() as Boolean)
}

这也是开始您自己的动画的好地方 运行 它们在 ViewHolders 视图上(记得覆盖适配器 onViewRecycled(holder: ViewHolder) 和 cancel/undo 它们的状态)