버튼 수집상

[안드로이드] notify 호출 후 ViewPropertyAnimator 에서 알파값이 변하지 않는 이슈 본문

TIL - 안드로이드

[안드로이드] notify 호출 후 ViewPropertyAnimator 에서 알파값이 변하지 않는 이슈

cocokaribou 2023. 6. 15. 09:49

배경

리사이클러 뷰 onScrolled 에서 뷰홀더가 화면에 일정 퍼센트 이상 노출됐을 때
알파값과 translationY 값을 제어해서

부드럽게 나타나는 것처럼 보이는 애니메이션이 적용되어있다.

open class CustomRecyclerView @JvmOverloads constructor(
    context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : RecyclerView(context, attrs, defStyleAttr) {

    // 애니메이션 제어할 뷰홀더 담아놓는 리스트
    private val itemHolders: MutableList<ViewHolder> = mutableListOf()

    fun onAddViewHolder(viewHolder: BaseViewHolder) {
        itemHolders.add(viewHolder)
    }

    fun onRemoveViewHolder(viewHolder: BaseViewHolder) {
        viewHolder.itemView.apply {
            alpha = 1.0f
            translationY = 0f
        }
        itemHolders.remove(viewHolder)
    }

    override fun onScrolled(dx: Int, dy: Int) {
        super.onScrolled(dx, dy)

        val listIterator = itemHolders.listIterator()
        while (listIterator.hasNext()) {
            val vh = listIterator.next()
            val itemView = vh.itemView
            val percent = ((1.0F.coerceAtMost((height - itemView.top) / itemView.height.toFloat())) * 100).toInt()

            if (vh is BaseViewHolder && vh.preparedAnimOnScroll && dy >= 0 && percent > 15) {
                vh.preparedAnimOnScroll = false
                // 뷰홀더에 애니메이션 적용
                itemView.animate()
                    .setInterpolator(DecelerateInterpolator())
                    .alpha(1.0f)
                    .translationY(0f)
                    .setDuration(800)
                    .setUpdateListener { animation ->
                        // 특정 뷰홀더에서만 애니메이션 체크
                        if (vh is ChagingViewHolder) {
                            Log.v("check", "animatedValue : ${animation.animatedValue}")
                            Log.v("check", "alpha : ${itemView.alpha}")
                            Log.v("check", "translationY : ${itemView.translationY}")
                            Log.v("check", "--------------------------------------")
                        }
                    }
                    .start()
            } else {
                if (vh is BaseViewHolder && !vh.preparedAnimOnScroll && dy >= 0) {
                    if (percent < 15) {
                        vh.preparedAnimOnScroll = true
                        vh.itemView.apply {
                            alpha = 0f
                            translationY = 130.toPx.toFloat()
                        }
                    }
                }
            }
        }
    }
}

 

일부 뷰홀더는 특정 시점에 notify 함수로 UI를 업데이트하고 있다.

그런데 스크롤되는 도중에 notify 함수가 호출되면 알파값이 고정되어 더 이상 변하지 않았다.

즉, 스크롤을 내려서 뷰홀더가 화면 안에 들어오면 완전히 불투명해져야하는데

반투명한 상태로 남아있는 상황이 간헐적으로 발생됐다.

 

setUpdateListener 안 쪽에 찍어둔 로그

 V  --------------------------------------
 V  animatedValue : 0.12109375
 V  alpha : 0.12109375
 V  translationY : 342.77344
 V  --------------------------------------
 V  animatedValue : 0.16048598
 V  alpha : 0.16048598
 V  translationY : 327.41046
 V  --------------------------------------
 V  animatedValue : 0.19673592
 V  alpha : 0.19673592
 V  translationY : 313.273
 V  --------------------------------------
 V  animatedValue : 0.234375
 V  alpha : 0.234375
 V  translationY : 298.59375
 V  --------------------------------------
 V  animatedValue : 0.27111095
 V  alpha : 0.27111095
 V  translationY : 284.26672
 V  --------------------------------------
 V  ===========notify view holder===========
 V  animatedValue : 0.30486095
 V  alpha : 0.30486095
 V  translationY : 271.10425
 V  --------------------------------------
 V  animatedValue : 0.0
 V  alpha : 0.30486095
 V  translationY : 271.0
 V  --------------------------------------
 V  animatedValue : 0.0
 V  alpha : 0.30486095
 V  translationY : 271.0
 V  --------------------------------------
 V  animatedValue : 0.01007247
 V  alpha : 0.30486095
 V  translationY : 268.27036
 V  --------------------------------------
 V  animatedValue : 0.04237941
 V  alpha : 0.30486095
 V  translationY : 259.51517
 V  --------------------------------------

notify view holder 로그가 찍힌 이후로 alpha 값이 변하지 않는 것을 확인할 수 있다.

반면 translationY는 제대로 변하고 있다.

 

해결

view.animate() 함수로 호출하는 ViewPropertyAnimator 는 애니메이션 타겟뷰의 속성값을 제어하는 애니메이터 클래스다.

notify 함수가 호출되면 뷰홀더의 뷰가 다시 그려지기 때문에

뷰의 속성에 접근하는 ViewPropertyAnimator 에도 영향을 준 것으로 보인다.

 

그래서 notify 이후 새로 그려지는 뷰에 영향을 받지 않는 ValueAnimator로 수정했다.

//...
val animator = ValueAnimator.ofFloat(0f, 1f).apply {
    duration = 800
    interpolator = DecelerateInterpolator()
    addUpdateListener {
        val alpha = it.animatedValue as? Float ?: 0f
        val translationY = (130.toPx).toFloat() - (130.toPx).toFloat() * alpha

        itemView.alpha = alpha
        itemView.translationY = translationY
        
        if (vh is ChagingViewHolder) {
            Log.v("check", "animatedValue : ${animation.animatedValue}")
            Log.v("check", "alpha : ${itemView.alpha}")
            Log.v("check", "translationY : ${itemView.translationY}")
            Log.v("check", "--------------------------------------")
        }
    }
}

animator.start()

이러면 notify 이후로 알파값이 문제없이 잘 변하는 것을 확인할 수 있다.

로그

 V  --------------------------------------
 V  animatedValue : 0.4375
 V  alpha : 0.4375
 V  translationY : 219.375
 V  --------------------------------------
 V  animatedValue : 0.46892345
 V  alpha : 0.46892345
 V  translationY : 207.11986
 V  --------------------------------------
 V  animatedValue : 0.49767345
 V  alpha : 0.49767345
 V  translationY : 195.90735
 V  --------------------------------------
 V  animatedValue : 0.52734375
 V  alpha : 0.52734375
 V  translationY : 184.33594
 V  --------------------------------------
 V  ===========notify view holder===========
 V  animatedValue : 0.556111
 V  alpha : 0.556111
 V  translationY : 173.11671
 V  --------------------------------------
 V  animatedValue : 0.5823609
 V  alpha : 0.5823609
 V  translationY : 162.87924
 V  --------------------------------------
 V  animatedValue : 0.609375
 V  alpha : 0.609375
 V  translationY : 152.34375
 V  --------------------------------------

 

그렇다면 ViewPropertyAnimator는 언제 쓰면 좋을까?

ViewPropertyAnimator는 신택스가 간단하고 메소드 체이닝을 지원한다.

그래서 하나의 타겟뷰의 translation, rotation, scale, alpha 을 조작하는 단순한 애니메이션을 적용할 때 사용하면 좋다.

 

참고

https://medium.com/mobile-app-development-publication/which-android-animator-to-use-ced54e21d317

 

Which Android Animator Is the Right One to Use?

A guide to decide which Animator class you need

medium.com

 

728x90