일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- ChatGPT
- 안드로이드
- DiffUtil.ItemCallback
- list map
- FastAPI
- android
- 유튜브
- llm
- android exoplayer
- ListAdapter
- AWS EC2
- kotlin list
- kotlin collection
- 독서
- video caching
- android ktor
- 스피너
- ListAdapter DiffUtil
- ktor api call
- Python
- exoplayer cache
- map
- 시행착오
- Zsh
- ExoPlayer
- build with ai
- doc2vec
- getChangePayload
- ktor client
- android custom view
- Today
- Total
버튼 수집상
[안드로이드] notify 호출 후 ViewPropertyAnimator 에서 알파값이 변하지 않는 이슈 본문
[안드로이드] 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
'TIL - 안드로이드' 카테고리의 다른 글
[안드로이드] RecyclerView에 radius 적용하기 (0) | 2023.07.03 |
---|---|
[안드로이드] ViewPager 내부 웹뷰 가로 스크롤 처리하기 (0) | 2023.06.21 |
[안드로이드] TabLayout.Tab 레이아웃에 커스텀뷰 세팅 + 뷰 추가하기 (0) | 2023.06.12 |
[안드로이드] 싱글액티비티 구조를 언제 쓰면 좋을까? (0) | 2023.05.23 |
[안드로이드] onBindViewHolder()가 호출될 때마다 뷰홀더 내부 가로 스크롤이 리셋되는 이슈 (0) | 2023.05.17 |