android - RecyclerView 最初出现时不会为项目设置动画,只有当它再次被回收时
问题描述
我在 aTranslateAnimation
的一个项目中有一个视图RecyclerView.Adapter
。动画应在最初出现时应用于特定的列表项,但仅在您向后滚动并再次回收该项目时才有效。
我的猜测是它与 RecyclerView 的生命周期有关,但我无法弄清楚是什么导致动画无法启动。
class mAdapter(items: List<String>): RecyclerView.Adapter<mAdapter.ViewHolder>(){
private var mPosition = 0
// The animation will be applied to the first item
override fun getItemViewType(position: Int): Int {
if (position == mPosition){
return 1
} else {
return 0
}
}
override fun onViewRecycled(holder: ViewHolder) {
super.onViewRecycled(holder)
// Animate the view inside the item
if (holder.itemViewType == 1){
holder.animateItem()
}
}
override fun onBindViewHolder(holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
super.onBindViewHolder(holder, position, payloads)
// Animate the view inside the item
if (holder.itemViewType == 1){
holder.animateItem()
}
}
inner class ViewHolder(view: View): RecyclerView.ViewHolder(view){
val picture: ImageView? = view.findViewById(R.id.picture)
fun animateItem(){
val itemWidth: Float = itemView.width.toFloat()
val animator = TranslateAnimation(-itemWidth, 0f, 0f, 0f)
animator.repeatCount = 0
animator.interpolator = AccelerateInterpolator(1.0f)
animator.duration = 700
animator.fillAfter = true
background?.animation = animator
background?.startAnimation(animator)
}
}
}
如果我在其中记录一条消息animateItem
,它将在 RecycleView 加载时出现,但在我向下和向上滚动之前它不会动画。
回答
正如NSimon指出的那样,解决方案是编写一个addOnGlobalLayoutListener
override fun onBindViewHolder(holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
holder.itemView.viewTreeObserver.addOnGlobalLayoutListener{
holder.animateItem()
}
}
解决方案
正如评论中所讨论的,您的第一个动画实际上正在正常播放。但是,第一次调用时,视图并没有完全分层显示在屏幕上。因此,itemView.width.toFloat()
返回 0,并且您正在从 0 动画到 0。
快速的解决方案是将动画的启动封装在 GlobalLayoutListener 中(这是系统告诉您视图已分层)。
像这样的东西:
override fun onBindViewHolder(holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
holder.itemView.viewTreeObserver.addOnGlobalLayoutListener{
holder.animateItem()
}
}
但是请记住,一旦开始动画,您应该删除 globalLayoutListener (否则它会永远留在那里,并且如果/当视图发生某些事情时会继续触发)。所以更好的方法是创建一个像这样的辅助函数:
inline fun <T: View> T.afterMeasured(crossinline f: T.() -> Unit) {
viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
if (measuredWidth > 0 && measuredHeight > 0) {
viewTreeObserver.removeOnGlobalLayoutListener(this)
f()
}
}
})
}
并像这样在 onBindViewHolder 内部调用它:
override fun onBindViewHolder(holder: ViewHolder, position: Int, payloads: MutableList<Any>) {
holder.itemView.afterMeasured{
holder.animateItem()
}
}
推荐阅读
- c# - RDLC 报告 C#。通过调节显示或隐藏控件
- python - 如何将openssl连接到Python?
- python - 将 \ucweirdnumberscharacters 翻译成英文?
- python - Python 字典中键的值未更新
- r - 当我使用 as.POSIXct 命令时,该列变为空
- git - ExtJS 6 将 workspace.json 添加到 gitignore
- mercurial - 如何判断存储库是否有来自 shell 脚本的传出更改并安全地检测到真正的错误?
- git - AWS CodeCommit - 致命:未找到存储库“https://git-codecommit.us-east-1..”
- php - CURL:主机名和端口后的路径
- sql - 使用 MS Access 或 SQL Server 的考勤日志