关闭 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 它们的状态)
我有一个视图,我想在点击时更改它的大小。
我有以下测试布局:
<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 它们的状态)