티스토리 뷰

반응형
SMALL

복잡한 애니메이션 처리를 하다가 콜백지옥(?) 이거나 아니면 Listener 를 중첩으로 쓰다보니 코드가 잘 읽혀지지 않는 문제가 생겨 코드 리펙토링을 하다보니 좋은 방법이 있어서 기록한다.

 

반응형

 

as -is 간단한(?) 애니메이션 코드

ValueAnimator.ofInt(100, 0).apply {
            addUpdateListener {
                progress_public_cheer.progress = it.animatedValue as Int
                //tv_public_cheer_count.text = (it.animatedValue as Int).toString()
            }
            addListener(object : AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator?) {
                    super.onAnimationEnd(animation)
                    ObjectAnimator.ofFloat(iv_public_cheer, View.ALPHA, 1f, 0f).apply {
                        addListener(object : AnimatorListenerAdapter() {
                            override fun onAnimationEnd(animation: Animator?) {
                                super.onAnimationEnd(animation)
                                val alpha = PropertyValuesHolder.ofFloat(View.ALPHA, 0f, 1f)
                                val scaleX = PropertyValuesHolder.ofFloat(View.SCALE_X, 0f, 1f)
                                val scaleY = PropertyValuesHolder.ofFloat(View.SCALE_Y, 0f, 1f)
                                ObjectAnimator.ofPropertyValuesHolder(
                                    iv_public_cheer,
                                    alpha,
                                    scaleX,
                                    scaleY
                                ).apply {
                                    addListener(object : AnimatorListenerAdapter() {
                                        override fun onAnimationStart(animation: Animator?) {
                                            super.onAnimationStart(animation)
                                            iv_public_cheer.setImageDrawable(
                                                ContextCompat.getDrawable(
                                                    requireContext(),
                                                    R.drawable.icon_public_cheer
                                                )
                                            )
                                        }
                                    })
                                    duration = 100L
                                    interpolator = OvershootInterpolator()
                                }.start()
                            }
                        })
                        duration = 100L
                        interpolator = AnticipateInterpolator()
                    }.start()
                }
            })
            duration = 500L
        }.start()

 

음.. 어떤 값이 100 에서 0으로 0.5만에 내려가면서 progress 를 줄인다.. 

이게 끝나는 순간 어떤 뷰를 알파 값을 바꾸고 ? 이 알파값 바꾸는게 끝나면 

알파값과 스케일을 조정해서 애니메이션을 한다 .. 

 

이게 이제 뭔가 여러개 조합이 되다보면 엄청 복잡해 진다 .. 

그래서 내가 찾은건 코루틴에서 제공해주는 await 함수를 써서 뭔가 대기 상태를 만들어 주려고 했다.

그중에 찾은 함수는 

suspendCancellableCoroutine

코루틴을 잠시 일시정지 시켜주는 함수이다.

 

아직 코루틴을 배우는 입장에서 적절하게 사용하는건지 모르겠지만 아주 잘 동작한다.

 

to-be 코드 

viewLifecycleOwner.lifecycleScope.launch {
            ValueAnimator.ofInt(100, 0).apply { duration = 500 }.run {
                addUpdateListener { progress_public_cheer.progress = it.animatedValue as Int }
                start()
                awaitEnd()
            }

            ObjectAnimator.ofFloat(iv_public_cheer, View.ALPHA, 1f, 0f).apply {
                duration = 100
                interpolator = AnticipateInterpolator()
            }.run {
                start()
                awaitEnd()
            }

            iv_public_cheer.setImageDrawable(
                ContextCompat.getDrawable(
                    requireContext(),
                    R.drawable.icon_public_cheer
                )
            )

            ObjectAnimator.ofPropertyValuesHolder(iv_public_cheer,
                PropertyValuesHolder.ofFloat(View.ALPHA, 0f, 1f),
                PropertyValuesHolder.ofFloat(View.SCALE_X, 0f, 1f),
                PropertyValuesHolder.ofFloat(View.SCALE_Y, 0f, 1f)).apply {
                duration = 100L
                interpolator = OvershootInterpolator()
            }.start()
        }

 

한눈에 봐도 아 이게 끝나면 저렇게 하는구나 라는게 딱 보이는거같아서 만족하는 코드가 됬다.

 

저 awaitEnd 함수는 

suspend fun Animator.awaitEnd() = suspendCancellableCoroutine<Unit> { cont ->
        cont.invokeOnCancellation { cancel() }
        addListener(object : AnimatorListenerAdapter() {
            private var endedSuccessfully = true
            override fun onAnimationCancel(animation: Animator) {
                endedSuccessfully = false
            }
            override fun onAnimationEnd(animation: Animator) {
                animation.removeListener(this)
                if (cont.isActive) {
                    if (endedSuccessfully) {
                        cont.resume(Unit, null)
                    } else {
                        cont.cancel()
                    }
                }
            }
        })
    }

 

suspendCancellableCoroutine

요게 이제 잠시 코루틴을 머춰 주고 끝나면 다시 resume 으로 코루틴을 재게 시키는 것! 

 

반응형
LIST
댓글