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

Android Animations Powered by Kotlin and RxJava2

Android Animations Powered by Kotlin and RxJava2

This talk was about effectively chaining Android animations using RxJava2 and Kotlin (for even further simplicity, everything still compatible with Java). When animating UI elements, the Android framework provides listeners for various animation events such as started, cancelled, stopped, etc. What we’ve done here was lifting the animation logic into the RxJava chains, thus making the whole process ‘reactive’ with much nicer syntax and better control flow management.

Ivan Škorić

October 27, 2017
Tweet

Other Decks in Programming

Transcript

  1. ValueAnimator ValueAnimator animation = ValueAnimator.ofFloat(0f, 100f);
 animation.setDuration(1000);
 animation.start(); animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener()

    {
 @Override
 public void onAnimationUpdate(ValueAnimator updatedAnimation) {
 float animatedValue = (float) updatedAnimation.getAnimatedValue();
 textView.setTranslationX(animatedValue);
 }
 });
  2. AnimatorSet AnimatorSet bouncer = new AnimatorSet();
 bouncer.play(bounceAnim).before(squashAnim1);
 bouncer.play(squashAnim1).with(squashAnim2);
 bouncer.play(squashAnim1).with(stretchAnim1);
 bouncer.play(squashAnim1).with(stretchAnim2);


    bouncer.play(bounceBackAnim).after(stretchAnim2);
 
 ValueAnimator fadeAnim = ObjectAnimator.ofFloat( newBall, "alpha", 1f, 0f);
 fadeAnim.setDuration(250);
 AnimatorSet animatorSet = new AnimatorSet();
 animatorSet.play(bouncer).before(fadeAnim);
 animatorSet.start();
  3. ViewPropertyAnimator textView.animate()
 .x(50f)
 .y(100f)
 .setDuration(1000)
 .setInterpolator(new AccelerateDecelerateInterpolator())
 .setStartDelay(50)
 .setListener(new Animator.AnimatorListener()

    {
 @Override public void onAnimationStart(Animator animator) {}
 @Override public void onAnimationEnd(Animator animator) {}
 @Override public void onAnimationCancel(Animator animator) {}
 @Override public void onAnimationRepeat(Animator animator) {}
 });
  4. ViewPropertyAnimator textView.animate()
 .x(50f)
 .y(100f)
 .setDuration(1000)
 .setInterpolator(new AccelerateDecelerateInterpolator())
 .setStartDelay(50)
 .setListener(new Animator.AnimatorListener()

    {
 @Override public void onAnimationStart(Animator animator) {}
 @Override public void onAnimationEnd(Animator animator) {}
 @Override public void onAnimationCancel(Animator animator) {}
 @Override public void onAnimationRepeat(Animator animator) {}
 });
  5. ViewPropertyAnimator textView.animate()
 .translationX(50f)
 .translationY(100f)
 .setDuration(1000)
 .setInterpolator(new AccelerateDecelerateInterpolator())
 .setStartDelay(50)
 .setListener(new Animator.AnimatorListener()

    {
 @Override public void onAnimationStart(Animator animator) {}
 @Override public void onAnimationEnd(Animator animator) {}
 @Override public void onAnimationCancel(Animator animator) {}
 @Override public void onAnimationRepeat(Animator animator) {}
 });
  6. ViewPropertyAnimator textView.animate()
 .translationX(50f)
 .translationY(100f)
 .setDuration(1000)
 .setInterpolator(new AccelerateDecelerateInterpolator())
 .setStartDelay(50)
 .setListener(new Animator.AnimatorListener()

    {
 @Override public void onAnimationStart(Animator animator) {}
 @Override public void onAnimationEnd(Animator animator) {}
 @Override public void onAnimationCancel(Animator animator) {}
 @Override public void onAnimationRepeat(Animator animator) {}
 });
  7. ViewPropertyAnimator textView.animate()
 .translationX(50f)
 .translationY(100f)
 .setDuration(1000)
 .setInterpolator(new AccelerateDecelerateInterpolator())
 .setStartDelay(50)
 .setListener(new Animator.AnimatorListener()

    {
 @Override public void onAnimationStart(Animator animator) {}
 @Override public void onAnimationEnd(Animator animator) {}
 @Override public void onAnimationCancel(Animator animator) {}
 @Override public void onAnimationRepeat(Animator animator) {}
 });
  8. Few remarks • Animation is scheduled to run as soon

    as animate() is called, start() is optional
  9. Few remarks • Animation is scheduled to run as soon

    as animate() is called, start() is optional • Only one animator can animate a specified view at a particular time
  10. Why RxJava? • Library for composing asynchronous and event- based

    programs by using observable sequences • How can we use it to improve Android animations syntax and logic flow?
  11. Why RxJava? • Library for composing asynchronous and event- based

    programs by using observable sequences • How can we use it to improve Android animations syntax and logic flow? • By wrapping Android animators API and callbacks inside RxJava objects
  12. RxJava2 reactive base types • Observable, Flowable • void onSubscribe(Disposable

    d) • void onNext(T value) • void onCompleted() • void onError(Throwable e)
  13. RxJava2 reactive base types • Observable, Flowable • void onSubscribe(Disposable

    d) • void onNext(T value) • void onCompleted() • void onError(Throwable e)
  14. RxJava2 reactive base types • Single • void onSubscribe(Disposable d)

    • void onSuccess(T value) • void onError(Throwable e)
  15. RxJava2 reactive base types • Completable • void onSubscribe(Disposable d)

    • void onComplete() • void onError(Throwable e)
  16. fun fadeIn(view: View, duration: Long): Completable {
 return Completable.fromAction {

    
 ViewCompat.animate(view)
 .setDuration(duration)
 .alpha(1f)
 }
 }
  17. fun fadeIn(view: View, duration: Long): Completable { val animationSubject =

    CompletableSubject.create()
 ViewCompat.animate(view)
 .setDuration(duration)
 .alpha(1f)
 .withEndAction {
 animationSubject.onComplete()
 }
 
 return animationSubject
 }
 

  18. fun fadeIn(view: View, duration: Long): Completable {
 val animationSubject =

    CompletableSubject.create() animationSubject.doOnSubscribe {
 ViewCompat.animate(view)
 .setDuration(duration)
 .alpha(1f)
 .withEndAction {
 animationSubject.onComplete()
 }
 }
 
 return animationSubject
 }
  19. fun fadeIn(view: View, duration: Long): Completable {
 val animationSubject =

    CompletableSubject.create() return animationSubject.doOnSubscribe {
 ViewCompat.animate(view)
 .setDuration(duration)
 .alpha(1f)
 .withEndAction {
 animationSubject.onComplete()
 }
 }
 }
 

  20. button1.alpha = 0f
 button2.alpha = 0f
 button3.alpha = 0f
 button4.alpha

    = 0f val durationMs = 1000L
 fadeIn(button1, durationMs)
 .andThen(fadeIn(button2, durationMs))
 .andThen(fadeIn(button3, durationMs))
 .andThen(fadeIn(button4, durationMs))
 .subscribe()
  21. fun fadeIn(view: View, duration: Long): Completable {
 val animationSubject =

    CompletableSubject.create() return animationSubject.doOnSubscribe {
 ViewCompat.animate(view)
 .setDuration(duration)
 .alpha(1f)
 .withEndAction {
 animationSubject.onComplete()
 }
 }
 }
 

  22. fun fadeIn(view: View, duration: Long): Completable {
 val animationSubject =

    CompletableSubject.create() return animationSubject.doOnSubscribe {
 ViewCompat.animate(view)
 .setDuration(duration)
 .alpha(1f)
 .withEndAction {
 animationSubject.onComplete()
 }
 }
 }
 

  23. fun View.fadeIn(duration: Long): Completable {
 val animationSubject = CompletableSubject.create()
 return

    animationSubject.doOnSubscribe {
 ViewCompat.animate(this)
 .setDuration(duration)
 .alpha(1f)
 .withEndAction {
 animationSubject.onComplete()
 }
 }
 }
  24. fun View.fadeIn(duration: Long): Completable {
 val animationSubject = CompletableSubject.create()
 return

    animationSubject.doOnSubscribe {
 ViewCompat.animate(this)
 .setDuration(duration)
 .alpha(1f)
 .withEndAction {
 animationSubject.onComplete()
 }
 }
 }
  25. fun View.fadeIn(duration: Long = 1000L): Completable {
 val animationSubject =

    CompletableSubject.create()
 return animationSubject.doOnSubscribe {
 ViewCompat.animate(this)
 .setDuration(duration)
 .alpha(1f)
 .withEndAction {
 animationSubject.onComplete()
 }
 }
 }
 

  26. fun animate() {
 fadeInTogether(button1, button2)
 .andThen(fadeInTogether(button3, button4))
 .subscribe()
 }
 


    fun fadeInTogether(view1: View, view2: View): Completable {
 return view1.fadeIn()
 .mergeWith(view2.fadeIn())
 }
  27. fun animate() {
 val timeObservable = Observable.interval(100, TimeUnit.MILLISECONDS)
 val btnObservable

    = Observable.just(button1, button2, button3, button4)
 
 Observable.zip(timeObservable, btnObservable,
 BiFunction<Long, View, Disposable> { time, view -> view.fadeIn().subscribe() })
 .subscribe()
 }
  28. fun animate() {
 val timeObservable = Observable.interval(100, TimeUnit.MILLISECONDS)
 val btnObservable

    = Observable.just(button1, button2, button3, button4)
 
 Observable.zip(timeObservable, btnObservable,
 BiFunction<Long, View, Disposable> { time, view -> view.fadeIn().subscribe() })
 .subscribe()
 }
  29. fun animate() {
 val timeObservable = Observable.interval(100, TimeUnit.MILLISECONDS)
 val btnObservable

    = Observable.just(button1, button2, button3, button4)
 
 Observable.zip(timeObservable, btnObservable,
 BiFunction<Long, View, Disposable> { time, view -> view.fadeIn().subscribe() })
 .subscribe()
 }
  30. fun animate() {
 val timeObservable = Observable.interval(100, TimeUnit.MILLISECONDS)
 val btnObservable

    = Observable.just(button1, button2, button3, button4)
 
 Observable.zip(timeObservable, btnObservable,
 BiFunction<Long, View, Disposable> { _, view -> view.fadeIn().subscribe() })
 .subscribe()
 }
  31. fun animate() {
 val timeObservable = Observable.interval(100, TimeUnit.MILLISECONDS)
 val btnObservable

    = Observable.just(button1, button2, button3, button4)
 
 Observable.zip(timeObservable, btnObservable,
 BiFunction<Long, View, Disposable> { _, view -> view.fadeIn().subscribe() })
 .subscribe()
 }
  32. PSPDFKit-labs/VanGogh • Provides RxJava2 wrappers for Android animations • Contains

    some common pre-made animations like fading, rotating, moving, etc. (more to be added over time)
  33. PSPDFKit-labs/VanGogh • Provides RxJava2 wrappers for Android animations • Contains

    some common pre-made animations like fading, rotating, moving, etc. (more to be added over time) • More powerful reactive constructs
  34. PSPDFKit-labs/VanGogh • Provides RxJava2 wrappers for Android animations • Contains

    some common pre-made animations like fading, rotating, moving, etc. (more to be added over time) • More powerful reactive constructs • Adding issues and pull requests very welcome
  35. PSPDFKit-labs/VanGogh fun fadeIn(view: View): AnimationCompletable { return AnimationBuilder.forView(view) .alpha(1f) .duration(2000L)

    .buildCompletable() .doOnAnimationReady({view -> view.setAlpha(0f)}) .doOnAnimationStart({view -> textView.setText(“Started”)}) .doOnAnimationEnd({view -> textView.setText(“Ended”)}) }
  36. PSPDFKit-labs/VanGogh fun fadeIn(view: View): AnimationCompletable { return AnimationBuilder.forView(view) .alpha(1f) .duration(2000L)

    .buildCompletable() .doOnAnimationReady({view -> view.setAlpha(0f)}) .doOnAnimationStart({view -> textView.setText(“Started”)}) .doOnAnimationEnd({view -> textView.setText(“Ended”)}) }
  37. PSPDFKit-labs/VanGogh fun fadeIn(view: View): AnimationCompletable { return AnimationBuilder.forView(view) .alpha(1f) .duration(2000L)

    .build() .doOnAnimationReady({view -> view.setAlpha(0f)}) .doOnAnimationStart({view -> textView.setText(“Started”)}) .doOnAnimationEnd({view -> textView.setText(“Ended”)}) }
  38. PSPDFKit-labs/VanGogh fun fadeIn(view: View): AnimationCompletable { return AnimationBuilder.forView(view) .alpha(1f) .duration(2000L)

    .build().toCompletable() .doOnAnimationReady({view -> view.setAlpha(0f)}) .doOnAnimationStart({view -> textView.setText(“Started”)}) .doOnAnimationEnd({view -> textView.setText(“Ended”)}) }
  39. PSPDFKit-labs/VanGogh fun fadeIn(view: View): AnimationCompletable { return AnimationBuilder.forView(view) .alpha(1f) .duration(2000L)

    .buildCompletable() .doOnAnimationReady({view -> view.setAlpha(0f)}) .doOnAnimationStart({view -> textView.setText(“Started”)}) .doOnAnimationEnd({view -> textView.setText(“Ended”)}) }
  40. PSPDFKit-labs/VanGogh fun fadeIn(view: View): AnimationCompletable { return AnimationBuilder.forView(view) .alpha(1f) .duration(2000L)

    .buildCompletable() .doOnAnimationReady({view -> view.setAlpha(0f)}) .doOnAnimationStart({view -> textView.setText(“Started”)}) .doOnAnimationEnd({view -> textView.setText(“Ended”)}) }
  41. PSPDFKit-labs/VanGogh fun fadeIn(view: View): AnimationCompletable { return AnimationBuilder.forView(view) .alpha(1f) .duration(2000L)

    .buildCompletable() .doOnAnimationReady({view -> view.setAlpha(0f)}) .doOnAnimationStart({view -> textView.setText(“Started”)}) .doOnAnimationEnd({view -> textView.setText(“Ended”)}) }
  42. PSPDFKit-labs/VanGogh fun fadeIn(view: View): AnimationCompletable { return AnimationBuilder.forView(view) .alpha(1f) .duration(2000L)

    .buildCompletable() .doOnAnimationReady({view -> view.setAlpha(0f)}) .doOnAnimationStart({view -> textView.setText(“Started”)}) .doOnAnimationEnd({view -> textView.setText(“Ended”)}) }
  43. PSPDFKit-labs/VanGogh fun fadeIn(view: View): AnimationCompletable { return AnimationBuilder.forView(view) .alpha(1f) .duration(2000L)

    .buildCompletable() .doOnAnimationReady({it.setAlpha(0f)}) .doOnAnimationStart({view -> textView.setText(“Started”)}) .doOnAnimationEnd({view -> textView.setText(“Ended”)}) }
  44. PSPDFKit-labs/VanGogh fun fadeIn(view: View): AnimationCompletable { return AnimationBuilder.forView(view) .alpha(1f) .duration(2000L)

    .buildCompletable() .doOnAnimationReady({it.setAlpha(0f)}) .doOnAnimationStart({textView.setText(“Started”)}) .doOnAnimationEnd({textView.setText(“Ended”)}) }
  45. PSPDFKit-labs/VanGogh fun fadeIn(view: View): AnimationCompletable { return AnimationBuilder.forView(view) .alpha(1f) .duration(2000L)

    .buildCompletable() .doOnAnimationReady({it.setAlpha(0f)}) .doOnAnimationStart({textView.setText(“Started”)}) .doOnAnimationEnd({textView.setText(“Ended”)}) } fadeIn(someView).andThen(fadeIn(someOtherView)).subscribe()
  46. PSPDFKit-labs/VanGogh fun fadeIn(view: View): AnimationCompletable { return AnimationBuilder.forView(view) .alpha(1f) .duration(2000L)

    .buildCompletable() .doOnAnimationReady({it.setAlpha(0f)}) .doOnAnimationStart({textView.setText(“Started”)}) .doOnAnimationEnd({textView.setText(“Ended”)}) } fadeIn(someView).andThen(fadeIn(someOtherView)).subscribe() fadeIn(someView).mergeWith(fadeIn(someOtherView)).subscribe()
  47. PSPDFKit-labs/VanGogh fun fadeIn(view: View): AnimationCompletable { return AnimationBuilder.forView(view) .alpha(1f) .duration(2000L)

    .buildCompletable() .doOnAnimationReady({it.setAlpha(0f)}) .doOnAnimationStart({textView.setText(“Started”)}) .doOnAnimationEnd({textView.setText(“Ended”)}) } sequentially(fadeIn(someView), fadeIn(someOtherView), …).subscribe() fadeIn(someView).mergeWith(fadeIn(someOtherView)).subscribe()
  48. PSPDFKit-labs/VanGogh fun fadeIn(view: View): AnimationCompletable { return AnimationBuilder.forView(view) .alpha(1f) .duration(2000L)

    .buildCompletable() .doOnAnimationReady({it.setAlpha(0f)}) .doOnAnimationStart({textView.setText(“Started”)}) .doOnAnimationEnd({textView.setText(“Ended”)}) } sequentially(fadeIn(someView), fadeIn(someOtherView), …).subscribe() together(fadeIn(someView), fadeIn(someOtherView), …).subscribe()