Slide 1

Slide 1 text

Speaker l Android UI Animation 들이붓기 안성용 / 네이버웹툰

Slide 2

Slide 2 text

난이도 시간 유지보수 기피하는 이유?

Slide 3

Slide 3 text

No content

Slide 4

Slide 4 text

Animation UseCase 손쉽게 구현하기 목표

Slide 5

Slide 5 text

Loading... Animation

Slide 6

Slide 6 text

Loading... ProgressBar와 Drawable을 이용하여, 브랜드가 들어간 로딩 UI를 손쉽게 만들 수 있다. +

Slide 7

Slide 7 text

Loading...

Slide 8

Slide 8 text

Loading...

Slide 9

Slide 9 text

Loading...

Slide 10

Slide 10 text

Loading...

Slide 11

Slide 11 text

Loading... 하지만 컨텐츠가 표시되기 전에 항상 로딩 UI가 보이면 조금 과한 느낌을 줄 수 있다. ContentLoadingProgressBar는 컨텐츠를 불러오는 속도가 느릴 때만 로딩 UI를 보여준다. (예. API 응답이 느린 경우)

Slide 12

Slide 12 text

ContentLoadingProgressBar 동작 방식은 단순하다. 500ms 이내에 불러오면 로딩 UI를 보여주지 않고, 한번 보여진 UI는 최소 500ms동안 보여준다. 이를 참고하여, 각 서비스에 맞는 Widget을 구현해볼 수 있다. public class ContentLoadingProgressBar extends ProgressBar { private static final int MIN_SHOW_TIME = 500; // ms private static final int MIN_DELAY = 500; // ms public synchronized void show() { postDelayed(mDelayedShow, MIN_DELAY); } public synchronized void hide() { long diff = System.currentTimeMillis() - mStartTime; if (diff >= MIN_SHOW_TIME || mStartTime == -1) { setVisibility(View.GONE); } else { postDelayed(mDelayedHide, MIN_SHOW_TIME - diff); } } }

Slide 13

Slide 13 text

Progress ProgressBar와 Drawable을 이용하여, Progress Animation를 손쉽게 만들 수 있다. 재생, 녹음/녹화, 다운로드 등에 사용할 수 있다. +

Slide 14

Slide 14 text

Progress

Slide 15

Slide 15 text

Progress

Slide 16

Slide 16 text

Progress val progressBar: ProgressBar = ... progressBar.progress = currentProgress

Slide 17

Slide 17 text

Frame Animation 로딩 UI를 Frame Animation으로 보여주고 싶다면 AnimationDrawable을 이용하여 손쉽게 만들 수 있다. " " " " "

Slide 18

Slide 18 text

Frame Animation

Slide 19

Slide 19 text

Frame Animation val drawable: AnimationDrawable = ... imageView.setImageDrawable(drawable) drawable.start() drawable.stop() Activity나 Fragment 등의 Lifecycle에 따라 Animation 시작과 종료를 관리해줘야 한다.

Slide 20

Slide 20 text

Frame Animation Link: AnimatedImageView.java 이 때, AnimatedImageView를 이용하면 XML에 선언하는 형태로 조금 더 편하게 사용할 수 있다.

Slide 21

Slide 21 text

Notification Icon AnimationDrawable, AnimatedVectorDrawable 등을 사용하면 알림 아이콘에도 애니메이션을 보여줄 수 있다.

Slide 22

Slide 22 text

Notification Icon ... Notification.Builder(...) .setSmallIcon(R.drawable.stat_sys_download) ... .build()

Slide 23

Slide 23 text

Click!! Animation

Slide 24

Slide 24 text

AnimatedStateListDrawable View 상태에 따라 Drawable을 보여줄 때, 각 상태 사이에 Animation을 보여줄 수 있다. 예를 들어 BottomNavigationView, TabLayout 처럼 selected 상태를 지원하거나, 좋아요, 즐겨찾기 등 checked 상태를 지원하는 상황에 사용하기 적합하다.

Slide 25

Slide 25 text

AnimatedStateListDrawable

Slide 26

Slide 26 text

AnimatedStateListDrawable

Slide 27

Slide 27 text

AnimatedStateListDrawable

Slide 28

Slide 28 text

AnimatedStateListDrawable

Slide 29

Slide 29 text

AnimatedStateListDrawable ... 32 32 32 32 32 32 32 32

Slide 30

Slide 30 text

AnimatedStateListDrawable imageView.setOnClickListener { it.isSelected = true // or false }

Slide 31

Slide 31 text

+ AnimatedVectorDrawable Frame Animation을 사용하는 대신 VectorDrawable에 Animation을 구현할 수도 있다.

Slide 32

Slide 32 text

+ AnimatedVectorDrawable

Slide 33

Slide 33 text

+ AnimatedVectorDrawable

Slide 34

Slide 34 text

+ AnimatedVectorDrawable 300 600 900 rotation 0 scale scale

Slide 35

Slide 35 text

+ AnimatedVectorDrawable

Slide 36

Slide 36 text

+ AnimatedVectorDrawable

Slide 37

Slide 37 text

Slide 38

Slide 38 text

Slide 39

Slide 39 text

android:duration="300" android:propertyName="scaleY" android:valueFrom="1" android:valueTo="0.8" android:valueType="floatType" />

Slide 40

Slide 40 text

android:valueFrom="0" android:valueTo="720" android:valueType="floatType" /> + AnimatedVectorDrawable

Slide 41

Slide 41 text

+ AnimatedVectorDrawable imageView.setOnClickListener { it.isSelected = true }

Slide 42

Slide 42 text

Link: https://shapeshifter.design/ PathData 간 morph animation을 쉽게 구현할 수 있다. Shape Shifter

Slide 43

Slide 43 text

Ripple Material Design에서 제공하는 Ripple 효과를 구현해보자.

Slide 44

Slide 44 text

Ripple

Slide 45

Slide 45 text

Ripple

Slide 46

Slide 46 text

Ripple 배경이 투명한 항목에는 미리 정의된 속성을 사용하여, Ripple 효과를 지원할 수 있다.

Slide 47

Slide 47 text

StateListAnimator StateListDrawable과 다르게 View 상태 변경에 따라 Animation을 보여줄 수 있다.

Slide 48

Slide 48 text

StateListAnimator

Slide 49

Slide 49 text

StateListAnimator 여기서는 배경색 Animation에 사용했지만, Translation / Scale 처리에 사용하는 경우가 일반적이다. 예를 들어, Button, FAB 클릭 효과를 구현할 때 주로 쓰인다.

Slide 50

Slide 50 text

Transition~ Animation

Slide 51

Slide 51 text

SharedElements Activity 혹은 Fragment 간에 화면을 전환할 때, 콘텐츠가 이어지는 것처럼 보여줄 수 있다. 프로필 사진을 확대해서 보거나, 이미지 목록형 화면에서 사용하기에 적합하다.

Slide 52

Slide 52 text

SharedElements // ProfileActivity.kt val intent = Intent(this, ViewerActivity::class.java) intent.putExtra("resId", resId) ActivityCompat.startActivity( this, intent, ActivityOptionsCompat .makeSceneTransitionAnimation( this, view, view.transitionName ) .toBundle() )

Slide 53

Slide 53 text

SharedElements // ViewerActivity.kt override fun onCreate(savedInstanceState: Bundle?) { window.sharedElementEnterTransition = TransitionSet().apply { interpolator = OvershootInterpolator(0.7f) ordering = TransitionSet.ORDERING_TOGETHER addTransition(ChangeBounds().apply { pathMotion = ArcMotion() }) addTransition(ChangeTransform()) addTransition(ChangeClipBounds()) addTransition(ChangeImageTransform()) } super.onCreate(savedInstanceState) }

Slide 54

Slide 54 text

View Animation Activity 혹은 Fragment 간에 화면을 전환할 때, 단순한 Animation을 보여줄 수도 있다.

Slide 55

Slide 55 text

View Animation // In Activity activity.overridePendingTransition( R.anim.slide_in_right, // enterAnim R.anim.slide_out_left // exitAnim ) // In Fragment fragmentManager.beginTransaction() .setCustomAnimations( R.anim.slide_in_right, // enterAnim R.anim.slide_out_left, // exitAnim ) .replace(...) .commit()

Slide 56

Slide 56 text

View Animation

Slide 57

Slide 57 text

View Animation

Slide 58

Slide 58 text

View Animation

Slide 59

Slide 59 text

View Animation

Slide 60

Slide 60 text

View Animation

Slide 61

Slide 61 text

View Animation

Slide 62

Slide 62 text

View Animation

Slide 63

Slide 63 text

TransitionManager 상태에 따라 Layout이 변경되어야 하는 경우, TransitionManager를 이용할 수 있다. ConstraintLayout을 사용하고 있다면 이 때, 레이아웃 전체를 변경하는 것보다 Guideline 등을 이용하는 것이 편리하다. 사진 편집 모드나 폴더블 상태 등에서 사용하면 좋을 듯 하다.

Slide 64

Slide 64 text

TransitionManager val layout = binding.root val constraintSet = ConstraintSet().apply { clone(layout) if (fold) { setGuidelinePercent(R.id.fold_guideline, 0.5f) } else { setGuidelinePercent(R.id.fold_guideline, 1f) } } TransitionManager.beginDelayedTransition(layout) constraintSet.applyTo(layout)

Slide 65

Slide 65 text

이외에도… Animator, ViewPropertyAnimator, DynamicAnimation LayoutTransition, CircularReveal, MotionLayout 등 다양한 API들이 제공되고 있다. Link: Android Animation 11% 더 활용하기

Slide 66

Slide 66 text

이외에도… 아래 Google I/O 세션 영상을 보는 것을 추천한다. Link: Motional Intelligence: Build Smarter Animations (Google I/O'19) Animated Slide

Slide 67

Slide 67 text

감사합니다!