Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Android Animation 11% 더 활용하기

Android Animation 11% 더 활용하기

Android에서 제공되는 다양한 Animation을 살펴보고, 구현 방법을 익힙니다.
https://github.com/fornewid/android-animation-11p-more

Sungyong An

June 23, 2020
Tweet

More Decks by Sungyong An

Other Decks in Programming

Transcript

  1. animation 11% • "a method in which pictures are manipulated

    to appear as moving images." Animation Link: https://en.wikipedia.org/wiki/Animation
  2. animation 11% • Drawable Animation • StateListAnimator • ViewPropertyAnimator •

    DynamicAnimation • View Animation • Shared Elements • Lottie (3rd party) • Interpolator • Animator • LayoutTransition • CircularReveal • Transition • MotionLayout ❌ animation
  3. animation 11% Lottie (3rd party) Drawable Animation StateListAnimator ViewPropertyAnimator DynamicAnimation

    View Animation Shared Elements Interpolator Animator Transition CircularReveal MotionLayout LayoutTransition animation View inner Multiple views Single view Between screens
  4. animation 11% AnimationDrawable // AnimationDrawable.java private void setFrame( int frame,

    boolean unschedule, boolean animate) { ... selectDrawable(frame); ... scheduleSelf(this, SystemClock.uptimeMillis() + mAnimationState.mDurations[frame]); }
  5. animation 11% AnimationDrawable // AnimationDrawable.java private void setFrame( int frame,

    boolean unschedule, boolean animate) { ... selectDrawable(frame); ... scheduleSelf(this, SystemClock.uptimeMillis() + mAnimationState.mDurations[frame]); }
  6. animation 11% AnimationDrawable // AnimationDrawable.java private void setFrame( int frame,

    boolean unschedule, boolean animate) { ... selectDrawable(frame); ... scheduleSelf(this, SystemClock.uptimeMillis() + mAnimationState.mDurations[frame]); }
  7. animation 11% AnimatedStateListDrawable <selector> <item android:id="@+id/selected" android:drawable="@drawable/ic_battery_100" android:state_checked="true" /> <item

    android:id="@+id/unselected" android:drawable="@drawable/ic_battery_0" android:state_checked="false" /> </selector>
  8. animation 11% <animated-selector> <item android:id="@+id/selected" android:drawable="@drawable/ic_battery_100" android:state_checked="true" /> <item android:id="@+id/unselected"

    android:drawable="@drawable/ic_battery_0" android:state_checked="false" /> </animated-selector> AnimatedStateListDrawable
  9. animation 11% android:id="@+id/selected" android:drawable="@drawable/ic_battery_100" android:state_checked="true" /> <item android:id="@+id/unselected" android:drawable="@drawable/ic_battery_0" android:state_checked="false"

    /> <transition android:drawable="@drawable/ad_battery_select" android:fromId="@id/unselected" android:toId="@id/selected" /> <transition android:drawable="@drawable/ad_battery_unselect" android:fromId="@id/selected" android:toId="@id/unselected" /> </animated-selector> AnimatedStateListDrawable
  10. animation 11% <!-- res/drawable/ad_battery_select.xml --> <animation-list android:oneshot="true"> <item android:drawable="@drawable/ic_battery_0" android:duration="32"

    /> <item android:drawable="@drawable/ic_battery_20" android:duration="32" /> ... </animation-list> AnimatedStateListDrawable
  11. animation 11% + AnimatedVectorDrawable <selector> <item android:id="@+id/selected" android:drawable="@drawable/ic_nav_agenda" android:state_checked="true" />

    <item android:id="@+id/unselected" android:drawable="@drawable/ic_nav_agenda" android:state_checked="false" /> </selector>
  12. animation 11% <selector> <item android:id="@+id/selected" android:drawable="@drawable/ic_nav_agenda" android:state_checked="true" /> <item android:id="@+id/unselected"

    android:drawable="@drawable/ic_nav_agenda" android:state_checked="false" /> </selector> + AnimatedVectorDrawable
  13. animation 11% + AnimatedVectorDrawable <selector> <item android:id="@+id/selected" android:drawable="@drawable/ic_nav_agenda" android:state_checked="true" />

    <item android:id="@+id/unselected" android:drawable="@drawable/ic_nav_agenda" android:state_checked="false" /> </selector>
  14. animation 11% <animated-selector> <item android:id="@+id/selected" android:drawable="@drawable/ic_nav_agenda" android:state_checked="true" /> <item android:id="@+id/unselected"

    android:drawable="@drawable/ic_nav_agenda" android:state_checked="false" /> </animated-selector> + AnimatedVectorDrawable
  15. animation 11% <item android:id="@+id/selected" android:drawable="@drawable/ic_nav_agenda" android:state_checked="true" /> <item android:id="@+id/unselected" android:drawable="@drawable/ic_nav_agenda"

    android:state_checked="false" /> <transition android:drawable="@drawable/avd_nav_schedule_select" android:fromId="@id/unselected" android:toId="@id/selected" /> </animated-selector> + AnimatedVectorDrawable
  16. animation 11% <animated-vector> <aapt:attr name="android:drawable"> <vector android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">

    <path android:name="calendar" android:pathData="M 9.5 15 C 10.881 15 12 ..." android:fillColor="#ffffff" android:fillAlpha="0.7"/> </vector> </aapt:attr> <target android:name="calendar"> <aapt:attr name="android:animation"> + AnimatedVectorDrawable
  17. animation 11% <animated-vector> <aapt:attr name="android:drawable"> <vector android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">

    <path android:name="calendar" android:pathData="M 9.5 15 C 10.881 15 12 ..." android:fillColor="#ffffff" android:fillAlpha="0.7"/> </vector> </aapt:attr> <target android:name="calendar"> <aapt:attr name="android:animation"> <set> <objectAnimator android:propertyName="pathData" android:duration="400" android:valueFrom="M 9.5 15 C 10.881 15 12 13.881 12 12..." android:valueTo="M 9.49 14.6 C 10.607 14.6 11.99 13.481..." + AnimatedVectorDrawable
  18. animation 11% <animated-vector> <aapt:attr name="android:drawable"> <vector android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">

    <path android:name="calendar" android:pathData="M 9.5 15 C 10.881 15 12 ..." android:fillColor="#ffffff" android:fillAlpha="0.7"/> </vector> </aapt:attr> <target android:name="calendar"> <aapt:attr name="android:animation"> <set> <objectAnimator android:propertyName="pathData" android:duration="400" android:valueFrom="M 9.5 15 C 10.881 15 12 13.881 12 12..." android:valueTo="M 9.49 14.6 C 10.607 14.6 11.99 13.481..." android:valueType="pathType" + AnimatedVectorDrawable
  19. animation 11% android:pathData="M 9.5 15 C 10.881 15 12 ..."

    android:fillColor="#ffffff" android:fillAlpha="0.7"/> </vector> </aapt:attr> <target android:name="calendar"> <aapt:attr name="android:animation"> <set> <objectAnimator android:propertyName="pathData" android:duration="400" android:valueFrom="M 9.5 15 C 10.881 15 12 13.881 12 12..." android:valueTo="M 9.49 14.6 C 10.607 14.6 11.99 13.481..." android:valueType="pathType" android:interpolator="@android:interpolator/fast_out_slow_in"/> <objectAnimator android:propertyName="pathData" android:startOffset="420" android:duration="150" android:valueFrom="M 9.49 14.6 C 10.607 14.6 11.99 13.4..." android:valueTo="M 9.5 15 C 10.881 15 12 13.881 12 12.5..." android:valueType="pathType" + AnimatedVectorDrawable
  20. animation 11% <target android:name="calendar"> <aapt:attr name="android:animation"> <set> <objectAnimator android:propertyName="pathData" android:duration="400"

    android:valueFrom="M 9.5 15 C 10.881 15 12 13.881 12 12..." android:valueTo="M 9.49 14.6 C 10.607 14.6 11.99 13.481..." android:valueType="pathType" android:interpolator="@android:interpolator/fast_out_slow_in"/> <objectAnimator android:propertyName="pathData" android:startOffset="420" android:duration="150" android:valueFrom="M 9.49 14.6 C 10.607 14.6 11.99 13.4..." android:valueTo="M 9.5 15 C 10.881 15 12 13.881 12 12.5..." android:valueType="pathType" android:interpolator="@android:interpolator/fast_out_linear_in"/> <objectAnimator android:propertyName="pathData" android:startOffset="570" android:duration="150" + AnimatedVectorDrawable
  21. animation 11% <target android:name="calendar"> <aapt:attr name="android:animation"> <set> <objectAnimator android:propertyName="pathData" android:duration="400"

    android:valueFrom="M 9.5 15 C 10.881 15 12 13.881 12 12..." android:valueTo="M 9.49 14.6 C 10.607 14.6 11.99 13.481..." android:valueType="pathType" android:interpolator="@android:interpolator/fast_out_slow_in"/> <objectAnimator android:propertyName="pathData" android:startOffset="420" android:duration="150" android:valueFrom="M 9.49 14.6 C 10.607 14.6 11.99 13.4..." android:valueTo="M 9.5 15 C 10.881 15 12 13.881 12 12.5..." android:valueType="pathType" android:interpolator="@android:interpolator/fast_out_linear_in"/> <objectAnimator android:propertyName="pathData" android:startOffset="570" android:duration="150" + AnimatedVectorDrawable
  22. animation 11% android:interpolator="@android:interpolator/linear_out_slow_in"/> <objectAnimator android:propertyName="pathData" android:startOffset="750" android:duration="200" android:valueFrom="M 9.5 15.5

    C 11.318 15.5 12 14.081 12..." android:valueTo="M 9.5 15 C 10.881 15 12 13.881 12 12.5 ..." android:valueType="pathType" android:interpolator="@android:anim/overshoot_interpolator"/> <objectAnimator android:propertyName="fillAlpha" android:duration="300" android:valueFrom="0.7" android:valueTo="1" android:valueType="floatType" android:interpolator="@android:interpolator/fast_out_slow_in"/> </set> </aapt:attr> </target> </animated-vector> + AnimatedVectorDrawable
  23. animation 11% + AnimatedVectorDrawable <com.google.android.material.bottomnavigation.BottomNavigationView ... app:menu="@menu/navigation" /> <!-- res/menu/navigation.xml

    --> <menu> <item android:id="@+id/navigation_schedule" android:icon="@drawable/asld_nav_schedule" android:title="Schedule" /> </menu>
  24. animation 11% ProgressBar + RotateDrawable <layer-list> <item android:gravity="center"> <rotate android:drawable="@drawable/loading_custom_progress"

    android:pivotX="50%" android:pivotY="50%" android:fromDegrees="0" android:toDegrees="360" /> </item> <item android:drawable="@drawable/ic_android" android:gravity="center" /> </layer-list>
  25. animation 11% ProgressBar + RotateDrawable <layer-list> <item android:gravity="center"> <rotate android:drawable="@drawable/loading_custom_progress"

    android:pivotX="50%" android:pivotY="50%" android:fromDegrees="0" android:toDegrees="360" /> </item> <item android:drawable="@drawable/ic_android" android:gravity="center" /> </layer-list>
  26. animation 11% ProgressBar + RotateDrawable <layer-list> <item android:gravity="center"> <rotate android:drawable="@drawable/loading_custom_progress"

    android:pivotX="50%" android:pivotY="50%" android:fromDegrees="0" android:toDegrees="360" /> </item> <item android:drawable="@drawable/ic_android" android:gravity="center" /> </layer-list>
  27. animation 11% ProgressBar + RotateDrawable <layer-list> <item android:gravity="center"> <rotate android:drawable="@drawable/loading_custom_progress"

    android:pivotX="50%" android:pivotY="50%" android:fromDegrees="0" android:toDegrees="360" /> </item> <item android:drawable="@drawable/ic_android" android:gravity="center" /> </layer-list>
  28. animation 11% ProgressBar + ClipDrawable <layer-list> <item android:gravity="center_vertical" /> <item

    android:gravity="center_vertical"> <clip /> </item> </layer-list>
  29. animation 11% ProgressBar + ClipDrawable <layer-list> <item android:gravity="center_vertical"> <shape android:shape="rectangle">

    <corners android:radius="2dp" /> <size android:height="4dp" /> <solid android:color="#ebebeb" /> </shape> </item> <item android:gravity="center_vertical"> <clip /> </item> </layer-list>
  30. animation 11% ProgressBar + ClipDrawable <layer-list> <item android:gravity="center_vertical" /> <item

    android:gravity="center_vertical"> <clip android:clipOrientation="horizontal" android:gravity="start"> <shape android:shape="rectangle"> <corners android:radius="2dp" /> <size android:height="4dp" /> <gradient android:angle="180" android:endColor="@color/android4" android:startColor="@color/android1" /> </shape> </clip> </item>
  31. animation 11% <!-- res/animator/sla_fab.xml --> <selector> <item android:state_pressed="true"> <objectAnimator android:duration="100"

    android:propertyName="translationZ" android:valueTo="6dp" android:valueType="floatType" /> </item> <item> <objectAnimator android:duration="100" android:propertyName="translationZ" android:valueTo="0dp" android:valueType="floatType" /> StateList Animator
  32. animation 11% <!-- res/animator/sla_fab.xml --> <selector> <item android:state_pressed="true"> <objectAnimator android:duration="100"

    android:propertyName="translationZ" android:valueTo="6dp" android:valueType="floatType" /> </item> <item> <objectAnimator android:duration="100" android:propertyName="translationZ" android:valueTo="0dp" android:valueType="floatType" /> </item> </selector> StateList Animator
  33. animation 11% <!-- res/animator/sla_fab.xml --> <selector> <item android:state_pressed="true"> <set> <objectAnimator

    /> <objectAnimator android:duration="100" android:propertyName="scaleX" android:valueTo="1.1" android:valueType="floatType" /> <objectAnimator android:duration="100" android:propertyName="scaleY" android:valueTo="1.1" android:valueType="floatType" /> StateList Animator
  34. animation 11% view.animate() .translationX(100f) .setStartDelay(300L) // ms .setDuration(1000L) // ms

    .setInterpolator(Interpolators.ACCELERATE_DECELERATE) View Property Animator
  35. animation 11% Render into off-screen buffers. View Layer (Hardware vs

    Software) Link: Android Graphics Performance (Google I/O '13) API 11
  36. animation 11% Hardware Layer view.setLayerType(View.LAYER_TYPE_HARDWARE, null) // do animate! Link:

    https://developer.android.com/guide/topics/graphics/hardware-accel#layers
  37. animation 11% • Viewী Alphaܳ ࢎਊೞח ҃਋, Viewܳ ࢚ࣘ೧ࢲ falseܳ

    ߈ജೞח Ѫ੉ ࢿמী ਬܻೞ׮. • ױ, View ղࠗ ਃٜࣗ੉ Ҁ஖૑ ঋইঠ ৢ߄ܰѱ Ӓ۰૓׮. An optimization when alpha is set on a view. View#hasOverlappingRendering() Link: Hidden Cost of Transparency (100 Days of Google Dev) API 16
  38. animation 11% View#hasOverlappingRendering() <ConstraintLayout> <ImageView ... android:layout_marginEnd="40dp" android:layout_marginBottom="40dp" android:src="@drawable/user_icon" />

    <ImageView ... android:layout_marginStart="40dp" android:layout_marginTop="40dp" android:src="@drawable/user_icon" /> </ConstraintLayout>
  39. animation 11% View#hasOverlappingRendering() <ConstraintLayout> <ImageView ... android:layout_marginEnd="40dp" android:layout_marginBottom="40dp" android:src="@drawable/user_icon" />

    <ImageView ... android:layout_marginStart="40dp" android:layout_marginTop="40dp" android:src="@drawable/user_icon" /> </ConstraintLayout>
  40. animation 11% View#hasOverlappingRendering() <ConstraintLayout android:alpha="0.5"> <ImageView ... android:layout_marginEnd="40dp" android:layout_marginBottom="40dp" android:src="@drawable/user_icon"

    /> <ImageView ... android:layout_marginStart="40dp" android:layout_marginTop="40dp" android:src="@drawable/user_icon" /> </ConstraintLayout>
  41. animation 11% View#hasOverlappingRendering() <ConstraintLayout android:alpha="0.5"> <ImageView ... android:layout_marginEnd="40dp" android:layout_marginBottom="40dp" android:src="@drawable/user_icon"

    /> <ImageView ... android:layout_marginStart="40dp" android:layout_marginTop="40dp" android:src="@drawable/user_icon" /> </ConstraintLayout>
  42. animation 11% View#hasOverlappingRendering() <AlphaOptimizedConstraintLayout android:alpha="0.5"> <ImageView ... android:layout_marginEnd="40dp" android:layout_marginBottom="40dp" android:src="@drawable/user_icon"

    /> <ImageView ... android:layout_marginStart="40dp" android:layout_marginTop="40dp" android:src="@drawable/user_icon" /> </AlphaOptimizedConstraintLayout>
  43. animation 11% View#hasOverlappingRendering() < AlphaOptimizedConstraintLayout android:alpha="0.5"> <ImageView ... android:layout_marginEnd="40dp" android:layout_marginBottom="40dp"

    android:src="@drawable/user_icon" /> <ImageView ... android:layout_marginStart="40dp" android:layout_marginTop="40dp" android:src="@drawable/user_icon" /> </AlphaOptimizedConstraintLayout>
  44. animation 11% View#hasOverlappingRendering() class AlphaOptimizedConstraintLayout @JvmOverloads constructor( context: Context, attrs:

    AttributeSet? = null, defStyleAttr: Int = 0 ) : ConstraintLayout(context, attrs, defStyleAttr) { override fun hasOverlappingRendering(): Boolean { return false } } Link: AlphaOptimizedFrameLayout.java
  45. animation 11% SpringAnimation val spring = SpringAnimation( view, DynamicAnimation.TRANSLATION_X, 300f)

    .withSpringForceProperties { dampingRatio = SpringForce.DAMPING_RATIO_LOW_BOUNCY stiffness = SpringForce.STIFFNESS_LOW } spring.start()
  46. animation 11% SpringAnimation val spring = SpringAnimation( view, DynamicAnimation.TRANSLATION_X, 300f)

    .withSpringForceProperties { dampingRatio = SpringForce.DAMPING_RATIO_LOW_BOUNCY stiffness = SpringForce.STIFFNESS_LOW } spring.start()
  47. animation 11% SpringAnimation val spring = SpringAnimation( view, DynamicAnimation.TRANSLATION_X) .withSpringForceProperties

    { dampingRatio = SpringForce.DAMPING_RATIO_LOW_BOUNCY stiffness = SpringForce.STIFFNESS_LOW } spring.animateToFinalPosition(300f)
  48. animation 11% SpringAnimation val spring = SpringAnimation( view, DynamicAnimation.TRANSLATION_X) .withSpringForceProperties

    { dampingRatio = SpringForce.DAMPING_RATIO_LOW_BOUNCY stiffness = SpringForce.STIFFNESS_LOW } spring.animateToFinalPosition(300f) spring.animateToFinalPosition(0f)
  49. animation 11% SpringAnimation val spring = SpringAnimation( view, DynamicAnimation.TRANSLATION_X) .withSpringForceProperties

    { dampingRatio = SpringForce.DAMPING_RATIO_LOW_BOUNCY stiffness = SpringForce.STIFFNESS_LOW } spring.animateToFinalPosition(300f) spring.animateToFinalPosition(0f) spring.animateToFinalPosition(500f)
  50. animation 11% • 0f, 1f ࢎ੉੄ чਸ ੸੺ೞѱ ࠁрػ чਵ۽

    ߈ജೠ׮. • ઺р੄ ч਷ 0f ~ 1f ߧਤܳ ߩযզ ࣻ ੓૑݅, द੘җ ՘਷ 0f, 1fܳ ࠁ੢ೠ׮. • ױࣽ൤ Inputী Outputਸ ߈ജೞח Ѫ੉޲۽, Singletonਵ۽ ࢎਊೡ ࣻ ੓׮. Animation੉ ߸ചೞח ੿ب Interpolator
  51. animation 11% Interpolator // package android.animation; public interface TimeInterpolator {

    // input : 0 and 1.0 // output: The interpolation value. float getInterpolation(float input); } // package android.view.animation; public interface Interpolator extends TimeInterpolator { }
  52. animation 11% Interpolator Link: android/view/animation/AnimationUtils.java#408 • res/interpolator/{file_name}.xml <linearInterpolator /> <accelerateInterpolator

    /> <decelerateInterpolator /> <accelerateDecelerateInterpolator /> <cycleInterpolator /> <anticipateInterpolator /> <overshootInterpolator /> <anticipateOvershootInterpolator /> <bounceInterpolator /> <pathInterpolator /> -> LinearInterpolator -> AccelerateInterpolator -> DecelerateInterpolator -> AccelerateDecelerateInterpolator -> CycleInterpolator -> AnticipateInterpolator -> OvershootInterpolator -> AnticipateOvershootInterpolator -> BounceInterpolator -> PathInterpolator
  53. animation 11% Interpolator AnimationUtils.loadInterpolator( context, android.R.interpolator.linear) val animation: Animation =

    ... animation.setInterpolator( context, android.R.interpolator.linear) <objectAnimator android:interpolator="@android:interpolator/linear" ... />
  54. animation 11% PathInterpolator android.view.animation <!-- Quad --> <pathInterpolator android:controlX1="0.33" android:controlY1="0"

    /> <!-- Cubic --> <pathInterpolator android:controlX1="0.33" android:controlY1="0" android:controlX2="1" android:controlY2="1" /> <!-- Path --> <pathInterpolator android:pathData="M 0.0,0.0 l 1.0,0.0 l 0.0,1.0" /> API 21
  55. animation 11% PathInterpolator android.view.animation // Quad PathInterpolator(0.33f, 0f) // Cubic

    PathInterpolator(0.33f, 0f, 1f, 1f) // Path PathInterpolator(Path().apply { moveTo(0f,0f) lineTo(1f, 0f) lineTo(0f, 1f) }) API 21
  56. animation 11% PathInterpolatorCompat androidx.core.view.animation // Quad PathInterpolatorCompat.create(0.33f, 0f) // Cubic

    PathInterpolatorCompat.create(0.33f, 0f, 1f, 1f) // Path PathInterpolatorCompat.create(Path().apply { moveTo(0f,0f) lineTo(1f, 0f) lineTo(0f, 1f) }) implementation 'androidx.interpolator:interpolator:1.0.0' API 14
  57. animation 11% Easing Interpolator val EASE_IN_OUT_CUBIC by cubicBezier(0.645f, 0.045f, 0.355f,

    1f) private fun cubicBezier( x1: Float, y1: Float, x2: Float, y2: Float ): Lazy<Interpolator> { return lazy { PathInterpolatorCompat.create(x1, y1, x2, y2) } }
  58. animation 11% • XML ౵ੌ۽ ࢶ঱ೡ ࣻ ੓׮. • VSYNC৬

    োزೞৈ 60fps۽ ز੘ೠ׮. package android.animation Animator
  59. animation 11% AnimatorInflater Link: http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/ animation/AnimatorInflater.java#666 • res/animator/{file_name}.xml <set />

    <animator /> <objectAnimator /> <propertyValuesHolder /> -> AnimatorSet -> ValueAnimator -> ObjectAnimator -> PropertyValuesHolder[]
  60. animation 11% Animator abstract class Animator class AnimatorSet extends Animator

    class ValueAnimator extends Animator class ObjectAnimator extends ValueAnimator class TimeAnimator extends ValueAnimator
  61. animation 11% • ValueAnimatorо Choreographerܳ ੉ਊೠ׮. VSYNCܳ ੉ਊೞৈ, ٣झ೒ۨ੉ ೐ۨ੐

    ۪؊݂ झாેਸ ҙܻ Choreographer Link: https://developer.android.com/reference/android/view/Choreographer API 16
  62. animation 11% Choreographer val choreographer = Choreographer.getInstance() choreographer.postFrameCallback { //

    draw next frame } // Not this view.post() // But this view.postOnAnimation { ... }
  63. animation 11% Choreographer val choreographer = Choreographer.getInstance() choreographer.postFrameCallback { //

    draw next frame } // Not this view.invalidate() // But this view.postInvalidateOnAnimation()
  64. animation 11% public class ValueAnimator extends Animator implements AnimationHandler.AnimationFrameCallback {

    private void start(boolean playBackwards) { ... AnimationHandler.getInstance() .addAnimationFrameCallback(this, delay); ... } public final boolean doAnimationFrame(long frameTime) {} } public class AnimationHandler { Choreographer: Animator
  65. animation 11% private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() {

    @Override public void doFrame(long frameTimeNanos) { doAnimationFrame(getProvider().getFrameTime()); Choreographer.getInstance() .postFrameCallback(mFrameCallback); } }; public void addAnimationFrameCallback(...) { Choreographer.getInstance() .postFrameCallback(mFrameCallback); } } Choreographer: Animator
  66. animation 11% ... } public final boolean doAnimationFrame(long frameTime) {}

    } public class AnimationHandler { private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() { @Override public void doFrame(long frameTimeNanos) { doAnimationFrame(getProvider().getFrameTime()); Choreographer.getInstance() .postFrameCallback(mFrameCallback); } }; public void addAnimationFrameCallback(...) { Choreographer.getInstance() .postFrameCallback(mFrameCallback); Choreographer: Animator
  67. animation 11% public final boolean doAnimationFrame(long frameTime) {} } public

    class AnimationHandler { private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() { @Override public void doFrame(long frameTimeNanos) { doAnimationFrame(getProvider().getFrameTime()); Choreographer.getInstance() .postFrameCallback(mFrameCallback); } }; public void addAnimationFrameCallback(...) { Choreographer.getInstance() .postFrameCallback(mFrameCallback); } } Choreographer: Animator
  68. animation 11% public class ValueAnimator extends Animator implements AnimationHandler.AnimationFrameCallback {

    private void start(boolean playBackwards) { ... AnimationHandler.getInstance() .addAnimationFrameCallback(this, delay); ... } public final boolean doAnimationFrame(long frameTime) {} } public class AnimationHandler { private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() { @Override public void doFrame(long frameTimeNanos) { doAnimationFrame(getProvider().getFrameTime()); Choreographer: Animator
  69. animation 11% Animator ValueAnimator.ofFloat(0f, 1f).apply { repeatMode = ValueAnimator.REVERSE repeatCount

    = ValueAnimator.INFINITE duration = 1_000L interpolator = Interpolators.ACCELERATE_DECELERATE }
  70. animation 11% <animator android:valueType="floatType" android:valueFrom="0" android:valueTo="1" android:repeatMode="reverse" android:repeatCount="infinite" android:duration="1000" android:interpolator=

    "@android:interpolator/accelerate_decelerate" /> Animator ValueAnimator.ofFloat() ValueAnimator.ofInt() ValueAnimator.ofPropertyValuesHolder() ValueAnimator.ofArgb()
  71. animation 11% Animator ValueAnimator.ofFloat(0f, 1f).apply { repeatMode = ValueAnimator.REVERSE repeatCount

    = ValueAnimator.INFINITE duration = 1_000L interpolator = Interpolators.ACCELERATE_DECELERATE }
  72. animation 11% val animator = ValueAnimator.ofFloat(0f, 1f).apply { repeatMode =

    ValueAnimator.REVERSE repeatCount = ValueAnimator.INFINITE duration = 1_000L interpolator = Interpolators.ACCELERATE_DECELERATE } Animator
  73. animation 11% val animator = ValueAnimator.ofFloat(0f, 1f).apply { repeatMode =

    ValueAnimator.REVERSE repeatCount = ValueAnimator.INFINITE duration = 1_000L interpolator = Interpolators.ACCELERATE_DECELERATE } animator.addUpdateListener { updateUi(it.animatedFraction) // or updateUi(it.animatedValue as Float) } animator.removeAllUpdateListeners() animator.start() animator.cancel() Animator
  74. animation 11% Animator private fun updateUi(fraction: Float) { iconView.run {

    rotation = lerp(0f, 360f, fraction) alpha = lerp(1f, .5f, fraction) scaleX = lerp(1f, 2f, fraction) scaleY = lerp(1f, .5f, fraction) translationX = lerp(0f, maxTranslationX, fraction) translationY = lerp(0f, maxTranslationY, fraction) } bugView.translationX = lerp(0f, maxTranslationX, fraction) }
  75. animation 11% Lerp fun lerp(from: Float, to: Float, progress: Float):

    Float { return from + (to - from) * progress }
  76. animation 11% LayoutTransition // ViewGroup.java case R.styleable.ViewGroup_animateLayoutChanges: boolean animateLayoutChanges =

    a.getBoolean(attr, false); if (animateLayoutChanges) { setLayoutTransition(new LayoutTransition()); } break;
  77. animation 11% PropertyValuesHolder pvhLeft = PropertyValuesHolder.ofInt("left", 0, 1); PropertyValuesHolder pvhTop

    = PropertyValuesHolder.ofInt("top", 0, 1); PropertyValuesHolder pvhRight = PropertyValuesHolder.ofInt("right", 0, 1); PropertyValuesHolder pvhBottom = PropertyValuesHolder.ofInt("bottom", 0, 1); PropertyValuesHolder pvhScrollX = PropertyValuesHolder.ofInt("scrollX", 0, 1); PropertyValuesHolder pvhScrollY = PropertyValuesHolder.ofInt("scrollY", 0, 1); defaultChange = ObjectAnimator.ofPropertyValuesHolder((Object)null, pvhLeft, pvhTop, pvhRight, pvhBottom, pvhScrollX, pvhScrollY); defaultFadeIn = ObjectAnimator.ofFloat(null, "alpha", 0f, 1f); defaultFadeOut = ObjectAnimator.ofFloat(null, "alpha", 1f, 0f); LayoutTransition
  78. animation 11% container.layoutTransition.apply { setInterpolator(LayoutTransition.APPEARING, Interpolators.ACCELERATE_DECELERATE) setDuration(LayoutTransition.APPEARING, 300) } //

    LayoutTransition.APPEARING // LayoutTransition.DISAPPEARING // LayoutTransition.CHANGE_APPEARING // LayoutTransition.CHANGE_DISAPPEARING // LayoutTransition.CHANGING LayoutTransition
  79. animation 11% Transition <!-- res/transition/auto_transition.xml --> <autoTransition /> val transition

    = TransitionInflater.from(it.context) .inflateTransition(R.transition.auto_transition) TransitionManager.beginDelayedTransition(container, transition) outerBackground.visibility = View.VISIBLE innerBackground.visibility = View.VISIBLE
  80. animation 11% TransitionInflater Link: http://androidxref.com/9.0.0_r3/xref/frameworks/base/core/java/android/ transition/TransitionInflater.java#148 • res/transition/{file_name}.xml <autoTransition />

    <fade /> <changeBounds /> <slide /> <explode /> <changeImageTransform /> <changeTransform /> <changeClipBounds /> <transitionSet /> ... -> AutoTransition -> Fade -> ChangeBounds -> Slide -> Explode -> ChangeImageTransform -> ChangeTransform -> ChangeClipBounds -> TransitionSet ...
  81. animation 11% • TransitionValuesח View ё୓৬ View IDী ӝ߈ೞৈ ஭୊ػ׮.

    Transition public abstract class Transition { public abstract void captureStartValues( TransitionValues transitionValues); public abstract void captureEndValues( TransitionValues transitionValues); protected void animate(Animator animator) { ... } }
  82. animation 11% Motion Layout <androidx.constraintlayout.motion.widget.MotionLayout app:layoutDescription="@xml/motion_scene"> <ImageView /> <TextView />

    <LinearLayout> <TextView /> <TextView /> <TextView /> <TextView /> <TextView /> <TextView /> </LinearLayout> </androidx.constraintlayout.motion.widget.MotionLayout>
  83. animation 11% Motion Layout <!-- res/xml/motion_scene.xml --> <MotionScene> <ConstraintSet android:id="@+id/start"

    /> <ConstraintSet android:id="@+id/end" /> <Transition app:constraintSetStart="@id/start" app:constraintSetEnd="@id/end" /> </MotionScene>
  84. animation 11% Motion Layout <!-- res/xml/motion_scene.xml --> <MotionScene> <ConstraintSet android:id="@+id/start">

    <Constraint android:id="@+id/profile_description" android:alpha="1" /> </ConstraintSet> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@+id/profile_description" android:alpha="0" /> </ConstraintSet> <Transition /> </MotionScene>
  85. animation 11% Motion Layout <!-- res/xml/motion_scene.xml --> <MotionScene> <ConstraintSet />

    <ConstraintSet /> <Transition> <KeyFrameSet> <KeyAttribute android:alpha="0" app:framePosition="30" app:motionTarget="@id/profile_description" /> </KeyFrameSet> </Transition> </MotionScene>
  86. animation 11% Motion Layout // Start transition motionLayout.transitionToStart() motionLayout.transitionToEnd() //

    Reset transition motionLayout.setTransition(R.id.start, R.id.end) // Seekable transition motionLayout.progress = 0f // ~ 1f
  87. animation 11% // Show revealView.showContents() private fun View.showContents() { visibility

    = View.VISIBLE createCircularRevealOf(fab, 0f, radius) { duration = 300 interpolator = Interpolators.DECELERATE }.start() } // Hide revealView.hideContents() private fun View.hideContents() { Circular Reveal
  88. animation 11% createCircularRevealOf(fab, 0f, radius) { duration = 300 interpolator

    = Interpolators.DECELERATE }.start() } // Hide revealView.hideContents() private fun View.hideContents() { createCircularRevealOf(fab, radius, 0f) { duration = 300 interpolator = Interpolators.ACCELERATE doOnEnd { visibility = View.GONE } }.start() } private inline fun View.createCircularRevealOf( target: View, Circular Reveal
  89. animation 11% doOnEnd { visibility = View.GONE } }.start() }

    private inline fun View.createCircularRevealOf( target: View, startRadius: Float, endRadius: Float, block: Animator.() -> Unit ): Animator { return ViewAnimationUtils.createCircularReveal( this, target.centerX(), target.centerY(), startRadius, endRadius ).apply(block) } Circular Reveal
  90. animation 11% interface CircularRevealWidget class CircularRevealLinearLayout extends LinearLayout class CircularRevealRelativeLayout

    extends RelativeLayout class CircularRevealGridLayout extends GridLayout class CircularRevealFrameLayout extends FrameLayout class CircularRevealCoordinatorLayout extends CoordinatorLayout class CircularRevealCardView extends MaterialCardView Circular Reveal : MDC
  91. animation 11% • ViewPropertyAnimator৬ ਬࢎೞ׮. • Windowр Animationী ࢎਊೡ ࣻ

    ੓׮. • Activity <—> Activity • Fragment <—> Fragment package android.view.animation View Animation
  92. animation 11% View Animation <set> <alpha android:fromAlpha="1.0" android:toAlpha="0.5" /> <translate

    android:fromXDelta="0" android:toXDelta="100" android:fromYDelta="0" android:toYDelta="100" /> </set>
  93. animation 11% View Animation <set android:shareInterpolator="true" android:duration="1000" android:interpolator="@android:anim/accelerate_interpolator"> <alpha android:fromAlpha="1.0"

    android:toAlpha="0.5" /> <translate android:fromXDelta="0" android:toXDelta="100" android:fromYDelta="0" android:toYDelta="100" /> </set>
  94. animation 11% Window Animation activity.overridePendingTransition( R.anim.slide_in_right, // enterAnim R.anim.slide_out_left //

    exitAnim ) fragmentManager.beginTransaction() .setCustomAnimations( R.anim.slide_in_right, // enterAnim R.anim.slide_out_left, // exitAnim ) .replace(...) .commit()
  95. animation 11% Shared Elements // Between activities val activityOptions =

    ActivityOptionsCompat.makeSceneTransitionAnimation( activity, Pair.create(view, view.transitionName) ) ActivityCompat.startActivity(context, intent, activityOptions.toBundle()) // Between fragments fragmentManager.beginTransaction() .addSharedElement(view, view.transitionName) .replace(...) .setReorderingAllowed(true) .commit()
  96. animation 11% Shared Elements // Between activities val activityOptions =

    ActivityOptionsCompat.makeSceneTransitionAnimation( activity, Pair.create(view, view.transitionName) ) ActivityCompat.startActivity(context, intent, activityOptions.toBundle()) // Between fragments fragmentManager.beginTransaction() .addSharedElement(view, view.transitionName) .replace(...) .setReorderingAllowed(true) .commit()
  97. animation 11% Shared Elements // In Kotlin view.transitionName = context.getString(R.string.transition_name)

    <!-- In XML --> <View android:id="@+id/view" ... android:transitionName="@string/transition_name" />
  98. animation 11% Shared Elements <!-- res/transition/move_shared_element.xml --> <transitionSet> <changeBounds />

    <targets> <target android:targetId="@id/view" /> </targets> </transitionSet>
  99. animation 11% Shared Elements <!-- res/transition/move_shared_element.xml --> <transitionSet> <changeBounds />

    <arcMotion /> <targets> <target android:targetId="@id/view" /> </targets> </transitionSet>
  100. animation 11% Shared Elements class MasterFragment : Fragment() class DetailFragment

    : Fragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) sharedElementEnterTransition = ...(R.transition.move_shared_element) sharedElementReturnTransition = ...(R.transition.move_shared_element) } }
  101. animation 11% • View ղࠗ গפݫ੉࣌ • Drawable Animation •

    StateListAnimator • ױੌ View গפݫ੉࣌ • ViewPropertyAnimator • DynamicAnimation Summary • ৈ۞ View গפݫ੉࣌ • Animator (+ lerp) • Transition • MotionLayout • ചݶ р গפݫ੉࣌ • View Animation • SharedElements
  102. animation 11% • AnimatedVectorDrawable • ইې ܻࣗझܳ ࢎਊೞৈ Splash Animationਸ

    ҳഅ೧ࠁࣁਃ. • ic_splash.svg • ic_splash_blink.svg • Tip: Shape Shifter ࢎਊೡ Ѫ Splash
  103. animation 11% Splash <animated-vector> <aapt:attr name="android:drawable"> <vector><path android:name="path_1" /></vector> </aapt:attr>

    <target android:name="path_1"> <aapt:attr name="android:animation"> <set> <objectAnimator android:propertyName="pathData" android:valueFrom="..." android:valueTo="..." android:valueType="pathType" /> <objectAnimator /> <objectAnimator /> </set> </aapt:attr> </target> </animated-vector> val avd = AnimatedVectorDrawableCompat .create(this, R.drawable.avd_splash) splashIcon.setImageDrawable(avd) avd?.start()
  104. animation 11% OnBoarding override fun onPageSelected(...) { pagerDescription.run { text

    = ... animateSlideUp() } } private fun View.animateSlideUp() { animate().cancel() alpha = 0f translationY = 40f animate() .alpha(1f) .translationY(0f) .setDuration(800) .setInterpolator(Interpolators.EASE_OUT_QUINT) .withLayer() .withEndAction(null) }
  105. animation 11% Main <animated-selector> <item android:id="@+id/selected" android:drawable="@drawable/ic_settings" android:state_checked="true" /> <item

    android:id="@+id/unselected" android:drawable="@drawable/ic_settings" android:state_checked="false" /> <transition android:drawable="@drawable/avd_settings_select" android:fromId="@id/unselected" android:toId="@id/selected" /> </animated-selector>
  106. animation 11% Main <animated-vector> <aapt:attr name="android:drawable"> <vector> <group android:name="group"><path /></group>

    </vector> </aapt:attr> <target android:name="group"> <aapt:attr name="android:animation"> <set> <objectAnimator android:duration="300" android:propertyName="rotation" android:valueFrom="0" android:valueTo="720" android:valueType="floatType" /> </set> </aapt:attr> </target> </animated-vector>
  107. animation 11% • MotionLayout • SeekableAnimatedVectorDrawable • ക ചݶਸ झ௼܀

    ೡ ٸ, ੹ജ Animationਸ ҳഅ೧ࠁࣁਃ. • ic_home_top.svg • ic_home_search.svg Home
  108. animation 11% Home <com.google.android.material.appbar.AppBarLayout> <com.google.android.material.appbar.CollapsingToolbarLayout app:layout_scrollFlags="scroll|exitUntilCollapsed|snap"> <androidx.appcompat.widget.Toolbar app:layout_collapseMode="pin" /> <include

    android:id="@+id/header" layout="@layout/home_header" /> </com.google.android.material.appbar.CollapsingToolbarLayout> </com.google.android.material.appbar.AppBarLayout> <androidx.constraintlayout.motion.widget.MotionLayout app:layoutDescription="@xml/home_header_scene"> <androidx.constraintlayout.widget.Guideline /> <TextView /> <ImageView /> </androidx.constraintlayout.motion.widget.MotionLayout>
  109. animation 11% Home <animated-vector> <aapt:attr name="android:drawable"> <vector> <path android:name="path_3" />

    <path android:name="path" /> </vector> </aapt:attr> <target android:name="path_3"> <aapt:attr name="android:animation"> <objectAnimator /> </aapt:attr> </target> <target android:name="path"> <aapt:attr name="android:animation"> <objectAnimator /> </aapt:attr> </target> </animated-vector>
  110. animation 11% Home var savd = SeekableAnimatedVectorDrawable .create(view.context, R.drawable.avd_search) header.icon.setImageDrawable(it)

    AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset -> val progress = -verticalOffset / appBarLayout.totalScrollRange.toFloat() binding.header.root.progress = progress savd?.currentPlayTime = (100 * progress).toLong() }
  111. animation 11% Detail <ImageView android:stateListAnimator="@animator/sla_fab" /> <selector> <item android:state_pressed="true"> <set

    android:ordering="together"> <objectAnimator android:propertyName="scaleX" android:valueTo="0.6" /> <objectAnimator android:propertyName="scaleY" android:valueTo="0.6" /> </set> </item> </selector> <item> <set android:ordering="together"> <objectAnimator android:propertyName="scaleX" android:valueTo="1" /> <objectAnimator android:propertyName="scaleY" android:valueTo="1" /> </set> </item>
  112. animation 11% Detail private class Heart( var targetX: Float =

    0f, var targetY: Float = 0f, var x: Float = 0f, var y: Float = 0f, var alpha: Float = 0f ) hearts.forEach { heart -> heart.targetX = lerp(drawableSize, width - drawableSize, random.nextFloat()) heart.targetY = lerp(drawableSize, height / 2, random.nextFloat()) } animator.start() fun lerp(from: Float, to: Float, progress: Float): Float { return (1 - progress) * from + to * progress }
  113. animation 11% Detail val animator = ValueAnimator.ofFloat(0f, 1f).apply { duration

    = 1000 interpolator = LinearInterpolator() addUpdateListener { ... } } override fun onDraw(canvas: Canvas) { hearts.forEach { heart -> canvas.withTranslation(heart.x, heart.y) { drawable.alpha = (255 * heart.alpha).toInt() drawable.draw(canvas) } } }
  114. animation 11% Detail val moveInterpolator = FastOutSlowInInterpolator() val alphaInterpolator =

    AccelerateInterpolator() addUpdateListener { val moveProgress = moveInterpolator.getInterpolation(it.animatedFraction) val alphaProgress = alphaInterpolator.getInterpolation(it.animatedFraction) val startX = width / 2f val startY = (height - drawableSize).toFloat() hearts.forEach { heart -> heart.x = lerp(startX, heart.targetX, moveProgress) heart.y = lerp(startY, heart.targetY, moveProgress) heart.alpha = lerp(1f, 0f, alphaProgress) } invalidate() }
  115. animation 11% Settings val panelAnim = SpringAnimation(panelView, DynamicAnimation.TRANSLATION_X) .withSpringForceProperties {

    dampingRatio = SpringForce.DAMPING_RATIO_LOW_BOUNCY stiffness = SpringForce.STIFFNESS_LOW } arrow.setOnClickListener { if (expanded) { panelAnim.animateToFinalPosition(0f) } else { panelAnim.animateToFinalPosition(160.dp) } }
  116. animation 11% Login <FrameLayout android:animateLayoutChanges="true" android:background="@drawable/button" android:minWidth="48dp" android:minHeight="48dp"> <TextView android:id="@+id/login_label"

    android:layout_width="wrap_content" android:layout_height="48dp" android:text="۽Ӓੋ" /> <ProgressBar android:id="@+id/login_progress" android:visibility="invisible" /> </FrameLayout> <shape android:shape="rectangle"> <corners android:radius="24dp" /> <solid android:color="..." /> </shape>
  117. animation 11% Profile (1) class GridItemAnimator(private val spanCount: Int =

    1) : DefaultItemAnimator() { override fun animateAddImpl(holder: RecyclerView.ViewHolder) { holder.itemView.animate() .setInterpolator(DecelerateInterpolator()) .setStartDelay(calculateDelay(holder)) .setDuration(addDuration) .alpha(1f) ... .start() } } private fun calculateDelay(holder: ViewHolder): Long { val position = holder.bindingAdapterPosition val order = position / spanCount + position % spanCount return order * addDuration }
  118. animation 11% Profile (2) // ProfileActivity.kt val intent = Intent(this,

    ViewerActivity::class.java) intent.putExtra("resId", resId) val options = ActivityOptionsCompat .makeSceneTransitionAnimation(this, view, view.transitionName) ActivityCompat.startActivity(this, intent, options.toBundle()) <!-- profile_item_photo.xml --> <ImageView android:transitionName="@string/transition_name_photo" /> <!-- viewer_activity.xml --> <ImageView android:transitionName="@string/transition_name_photo" />
  119. animation 11% Profile (2) // 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) ... }
  120. animation 11% • Transition • ಫ؊࠶, ಞ૘ ١ ࢚క ੹ജী

    ٮۄ ۨ੉ইਓ Animationਸ ҳഅ೧ࠁࣁਃ. Viewer
  121. animation 11% Viewer 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)
  122. animation 11% Notification <animation-list android:oneshot="false"> <item android:drawable="@drawable/stat_sys_download_anim0" android:duration="200" /> <item

    android:drawable="@drawable/stat_sys_download_anim1" android:duration="200" /> ... </animation-list> Notification.Builder(...) .setSmallIcon(R.drawable.stat_sys_download) ... .build()