关闭RecyclerView子大小更改动画

时间:2018-01-14 14:50:13

标签: android android-recyclerview kotlin android-animation android-custom-view

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

我有以下测试布局:

<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

正如我所看到的,它也试图为尺寸变化设置动画 - 这是真的吗?

如何解决这个滞后问题?

3 个答案:

答案 0 :(得分:2)

docs of DefaultItemAnimator中所示:

  

RecyclerView.ItemAnimator 的此实现提供了有关删除,添加和移动 RecyclerView 中项目所发生事件的基本动画。 RecyclerView 默认使用 DefaultItemAnimator

如果要删除这些动画,请将默认动画设备清零:

recyclerview.itemAnimator = null

答案 1 :(得分:0)

您正在使用notifyItemChanged(position: Int),RecyclerView不知道究竟发生了什么变化 - 也许项目被另一个项目全部替换,因此新的ViewHolder被绑定在适配器onBindViewHolder(holder: ViewHolder, position:Int)中并使用默认动画替换旧的

如果你明确知道改变了什么,你可以使用notifyItemChanged(position: Int, payload: Any)为适配器提供更改有效负载,那么RecyclerView将只在适配器onBindViewHolder(holder: ViewHolder, position:Int, payloads: MutableList<Any>)中执行ViewHolder的部分更新,并且不会替换它。

例如,您可以更改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)并取消/撤消它们的状态)

答案 2 :(得分:-1)

您可以通过Item Animator执行此操作并初始化它们的项目动画