简体   繁体   中英

Android Animation Issue on RecycleView Items

I added on click animations on some button items from a recycle view.

Everything works smoothly, however when I scroll down the list some buttons appear in a weird state like they were frozen during the animation phase even though the animation was not being triggered for these buttons while for others the their view is set to invisible/gone.

This is part of the shrinking animation code, where shrink is a ValueAnimator object.

shrink.addUpdateListener { animation ->
        val animatedValue = animation.animatedValue as Int
        v.layoutParams.width = animatedValue
        v.requestLayout()
    }
    shrink.addListener(object : AnimatorListenerAdapter() {
        override fun onAnimationStart(animation: Animator) {
            super.onAnimationStart(animation)
            v.visibility = View.VISIBLE
            v.isEnabled = false
        }
        override fun onAnimationEnd(animation: Animator) {
            super.onAnimationEnd(animation)
            v.layoutParams.width = 0
            v.visibility = View.GONE
            v.isEnabled = false
        }
   }

I have a View and ViewHolder class where I bind the visibility of the recycle view items when I scroll.

Is there a step I missed in the animation lifecycle to work together with the recycle view items? I was thinking that maybe I have to take care of onAnimationStop or onAnimationPause cases but I am not sure.

Any help would be much appreciated.

I think that the cause of your problem is the fact that the views are recycled when you scroll. If the animation is triggered on a view and the list is scrolled the animation continues, but at some point, the view gets recycled and re-populated and it leads to the weird state.

The solution is to cancel the animation and set the initial state to the view in onBindViewHolder method of your adapter. The steps I would do in your case:

  1. Link your value animator to the view holder, so each view holder has its own value animator.
  2. Trigger the animation like you do now, using the view holder's local value animator.
  3. Cancel your value animator in onViewRecycled
  4. In onBindViewHolder explicitly set your view's visibility to visible, enable it and set full width.

You can use setHasTransientState to mark view as a kind of "dirty", so RecyclerView will not re-use it while animation is not finished. This should solve your issue. Here is the modified code:

shrink.addUpdateListener { animation ->
    val animatedValue = animation.animatedValue as Int
    v.layoutParams.width = animatedValue
    v.requestLayout()
}
shrink.addListener(object : AnimatorListenerAdapter() {
    override fun onAnimationStart(animation: Animator) {
        super.onAnimationStart(animation)
        v.setHasTransientState(true) // Mark view
        v.visibility = View.VISIBLE
        v.isEnabled = false
    }
    override fun onAnimationEnd(animation: Animator) {
        super.onAnimationEnd(animation)
        v.layoutParams.width = 0
        v.visibility = View.GONE
        v.isEnabled = false
        v.setHasTransientState(true) // "Return" it to the pool
    }

}

This is exactly what PropertyAnimation do under the hood. Hope this will help :)

Start by removing the default RecyclerView item animator

recyclerView.setItemAnimator(null);

And also cancel any custom running animation in the adapter onViewRecycled

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM