안드로이드/개발기록

[Android] 증가하는 시간 차 그래프 애니메이션 구현하기 - (3) ViewTreeObserver, OnScrollChangedListener

sxunea 2024. 10. 15. 23:59

 

 

 

 

[Android] 증가하는 시간 차 그래프 애니메이션 구현하기 - (2) ViewTreeObserver, GlobalLayoutListener

[Android] 증가하는 시간 차 그래프 애니메이션 구현하기 - (1) ValueAnimator안드로이드 개발을 하면 할수록 그 어떤 것 보다 예상 소요 시간 , 사용 기술을 확답할 수 없는 것이 바로 처음 보는 UI/UX 를

sxunea.tistory.com

 

증가하는 시간 차 그래프 애니메이션 구현하기 기록의 마지막 글로, 이전의 글은 위 링크를 클릭하면 참고할 수 있다. 

 

이전 두 개의 포스트를 통해 요구사항 중 

  • 사용자가 저 뷰를 봤을때 (스크롤해서 내려갔을때) 애니메이션이 시작되게 해주세요.

를 빼고는 모두 구현을 완료했다. 그렇다면 이번 글을 통해 위의 요구사항까지 만족해 구현을 끝내보자.

 

 

ViewTreeObserver Listener

Listener  Description 
ViewTreeObserver.OnDrawListener 뷰가 그려질 때 
ViewTreeObserver.OnGlobalFocusChangeListener 뷰 트리의 내의 포커스가 변경될 때 
ViewTreeObserver.OnGlobalLayoutListener 뷰 트리의 global layout 상태나 visibility 여부가 변경될 때
ViewTreeObserver.OnPreDrawListener 뷰 트리가 그려지려고 할 때
ViewTreeObserver.OnScrollChangedListener 뷰 트리 항목이 스크롤 되었을 때
ViewTreeObserver.OnTouchModeChangeListener 터치 모드가 변경될 때
ViewTreeObserver.OnWindowAttachListener 뷰 트리가 window에 attach 되거나 detach될 때
ViewTreeObserver.OnWindowFocusChangeListener 뷰 트리의 window focus 상태가 변경 될 때

 

이전 글에서 언급했지만, 한번 더 기억할 겸 리스너를 살펴보자. 

 

남은 요구사항은 단 하나, 스크롤의 위치에 따른 UI의 변화이다. 이를 위해서는 위의 리스너 중 onScrollChangedListener를 활용하면된다. 

 

onScrollChangedListener

 

ViewTreeObserver.OnScrollChangedListener는 Android에서 뷰가 스크롤될 때 이를 감지하고 처리할 수 있는 리스너이다. 주로 스크롤 위치에 따라 UI를 동적으로 업데이트하거나 특정 이벤트를 트리거할 때 사용된다. 

 

 

 

이 애니메이션은 하나의 프래그먼트에 존재하고, ScrollView에서 사용자가 이 뷰까지 스크롤 해 내려와야 증가 애니메이션을 실행해야 한다. 이 경우에 OnScrollChangedListener를 사용하면 된다. 

 

 

바로 구현 코드를 보자. 

 

fun startAnimationOnVisible(
    scrollView: ScrollView,
    targetView: TextView,
    animator: ValueAnimator
) {
    scrollView.viewTreeObserver.addOnScrollChangedListener {
        // 애니메이션이 이미 실행 중이면 return
        if (animator.isRunning) {
            return@addOnScrollChangedListener
        }
        
        val scrollY = scrollView.scrollY
        val location = IntArray(2)
        targetView.getLocationOnScreen(location)
        
        // targetView가 ScrollView 내에서 완전히 보이는 경우 애니메이션 시작
        if (location[1] > scrollY && location[1] + targetView.height < scrollY + scrollView.height) {
            animator.addListener(object : AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator) {
                    // 애니메이션 종료 후 추가 작업 가능
                }
            })
            animator.start()
        }
    }
}

 

 

스크롤 이벤트 감지

  • ScrollView의 viewTreeObserver를 사용하여 스크롤 변화가 발생할 때마다 리스너를 통해 처리한다.

 

중복 애니메이션 방지

  • isAnimating 배열을 사용하여 특정 뷰가 이미 애니메이션 중인지 확인한다.
  • 이때, 중복 실행을 막기 위해, 이미 애니메이션 중이라면 이벤트 처리를 중단한다.
    • isRunning 활용

 

뷰의 위치 확인

  • getLocationOnScreen() 메서드를 사용해 targetView의 화면 내 위치를 가져온다.
  • 이 값과 ScrollView의 scrollY 값을 비교하여 뷰가 화면에 완전히 노출된 경우에만 애니메이션을 시작한다.

 

애니메이션 시작 조건

  • targetView가 ScrollView의 가시 영역에 완전히 들어왔을 때만 애니메이션을 실행한다.

 

애니메이션 리스너 등록

  • animator에 리스너를 등록하여 애니메이션이 끝날 때 isAnimating 값을 false로 변경한다.
  • 이를 통해 같은 뷰에 대한 애니메이션이 중복 실행되지 않도록 한다.

 

 

이렇게 ScrollListener를 활용하면 스크롤 위치를 가져와서 원하는 시점에 맞는 동작을 구현할 수 있다.

 

하지만 ViewTreeObserver의 Listener들을 활용할 때는 리스너가 불필요한 경우에는 리스너를 해제해 주어 리소스 낭비 / 성능 저하를 막아야함을 기억하며 마치자. 끝 !