2019년 12월 31일 화요일

Complex UI/Animations on Android 정리

https://proandroiddev.com/complex-ui-animation-on-android-8f7a46f4aec4

1. 스크롤 할 때 툴바에 애니메이션 효과 주기

AppBarLayout의 behavior에 내가 사용하려는 애니메이션 효과를 가지는 커스텀 CoordinatorLayout behavior을 지정해준다.

아래와 같은 클래스를 만들고 onStartNestedScroll(스크롤이 시작할 때 호출되는 함수)과 onNestedScroll(스크롤중일 때 호출되는 함수)을 구현한다.

class ToolbarBehavior : CoordinatorLayout behavior<AppBaLayoutr>() { ... }

- onStartNestedScroll

위 아래로의 스크롤(vertical)일 때 true를 리턴한다.

- onNestedScroll

dyConsumed의 값이 때라 scroll up인지 down인지 파악한다. dyConsumed가 양수이면 scroll down이고, dyConsumed가 음수이면 scroll up임을 의미한다.

height를 변경하고 나면 requestLayout을 호출해 주어야 한다.
위치를 바꿀 때 translationX와 translationY를 사용한다.
scale을 적용할 때 scaleX와 scaleY를 사용한다.

추가로 ToolbarBehavior의 효과를 적용할 때 관련 뷰들을 참조하고 있어야 한다. 따라서, onStartNestedScroll이나 onNestedScroll이 호출되기 전에 이러한 관련 값(Views, height, 최소 height등)들을 미리 설정해 놓고 있어야 한다.

2. RecyclerView의 item의 height 변경할 때 애니메이션 효과 주기

아이템의 평상시 height와 커졌을 때의 height를 계산해놓고 있다가 사용자가 아이템을 클릭할 때 이에 맞추어 애니메이션 효과를 주는 것이 필요하다.

- Adapter의 onViewAttachedToWindow에서 height를 계산한다.

onViewAttachedToWindow는 Adapter에서 만든 뷰가 RecyclerView에 연결될 때 호출되는 함수이다.

- 평상시 height와 커졌을 때의 height를 계산하는 방법은 다음과 같다.

doOnLayout에서 평상시 뷰의 height를 계사한다. -> expanded view를 visible로 설정한다. -> doOnNextLayout에서 expanded view의 height를 계산한다. -> expanded view를 hide한다.

위와 같이 하면 사용자에게 뷰가 커졌다 작아졌다 하는 것을 보여주지 않고 각각의 height를 알 수 있게 된다.(이유는? : 아마도 onDraw가 불리기 전에 관련 layout이 실행되는 동안 height를 알아내서 사용자에게는 안보이게 되는 것이 아닐까 추측. 정확하게는 모르겠네요.) 여기서 한가지 더 주의할 것은 expanded view의 hide를 호출하는 부분을 expandedView.post { ... } 안(또는 doOnPreDraw에서)에서 해주어야 한다는 점이다.(expandedView.visible = false는 layout이 일어나도록 하지 않는다.)(post나 doOnPreDraw를 사용하면 onDraw가 불리기 전에 layout이 다시 일어나게 되는 걸까요? 이부분도 잘 모르겠군요.)

아이템을 클릭하면 이전에 커진 뷰가 있다면 원래 크기로 돌려야 하고 새로 클릭된 뷰를 크기를 키워야 한다. RecyclerView의 findViewHolderForAdapterPosition를 통해 이전에 커진 뷰를 찾을 수 있다.

ValueAnimator를 사용해서 점진적으로 커지는 애니메이션 효과를 줄 수 있다.

3. ViewPager2의 위치가 변경될 때 관련 탭의 위치도 같이 변경하기

ViewPager의 onPageScrolled에서 position과 positionOffset을 받아서 탭의 위치를 변경한다.(이 문서에서는 RecyclerView의 scrollBy를 사용해서 탭의 위치를 변경했는데 RecyclerView의 layout manager가 LinearLayoutManager라면 LinearLayoutManager의 scrollToPositionWithOffset를 사용하는 방법도 있다. 이게 더 간단한 방법인듯 하다.)

4. Filter sheet animation

네 단계로 나누어진 애니메이션 효과를 준다. 각각의 애니메이션이 동작하는 순서를 위해 AnimationSet을 사용한다.

a. Fab 버튼의 path animation
b. RecyclerView의 아이템들을 shrink하는 scale down animation
c. Fab 버튼이 Filter sheet로 변경되는 animation
d. 최종 뷰들 보여주기

각각을 자세히 살펴보자.

a. Fab 버튼의 path animation

현재 위치에서 화면의 중간으로 이동한다.
애니메이션을 위해 ValueAnimator를 사용하는데 이때의 progress를 ArcMetric를 사용해서 변경된 형태로 뷰의 x와 y에 적용한다.

b. RecyclerView의 아이템들을 shrink하는 scale down animation

ValueAnimator를 써서 현재 화면에 보이는 아이템들을 scale down 한다.

c. Fab 버튼이 Filter sheet로 변경되는 animation

Fab 버튼의 크기를 최종 뷰의 크기로 변경한다. 이때 0 ~ 0.8 까지는 원의 형태로 커지다가 0.8 ~ 1 로는 사각형으로 커지게 한다.

d. 최종 뷰들 보여주기

개별 뷰들을 화면에 보여준다.

5. Filtering animation

여섯 단계로 나누어진 애니메이션이다.

a. Fab Collapse Animation
b. Bottom Bar Animation
c. Tabs and ViewPager fade out animation
d. Close Icon rotate animation to simulate loading

rotation값을 변경한다.

e. Fab Arc Path animation
f. RecyclerView items scale down animation

댓글 없음:

댓글 쓰기

Building asynchronous views in SwiftUI 정리

Handling loading states within SwiftUI views self loading views View model 사용하기 Combine을 사용한 AnyPublisher Making SwiftUI views refreshable r...