Android Animation by ScheduledExecutorService

93bc8fb48f57c11e417dad9d26a2fb8a?s=47 petitviolet
September 29, 2015

Android Animation by ScheduledExecutorService

93bc8fb48f57c11e417dad9d26a2fb8a?s=128

petitviolet

September 29, 2015
Tweet

Transcript

  1. 非同期で頑張る アニメーション Roppongi.aar#1 2015/09/29 @petitviolet 

  2. About Me • Hiroki Komurasaki • @petitviolet • Fringe81 Co,.

    Ltd • Android Engineer 
  3. アニメーションしてますか 

  4. 

  5. Animation • 代表的なのはObjectAnimator *要出典 • PropertyValuesHolder • AnimatorSet • AnimationListener

    
  6. 

  7. PropertyValuesHolder biggerX = PropertyValuesHolder.ofFloat("scaleX", 1f, 2f);
 PropertyValuesHolder biggerY = PropertyValuesHolder.ofFloat("scaleY",

    1f, 2f);
 ObjectAnimator biggerAnim = ObjectAnimator.ofPropertyValuesHolder( mHelloWorld, biggerX, biggerY);
 biggerAnim.setDuration(1000);
 biggerAnim.addListener(new Animator.AnimatorListener() {
 // onAnimationXXX
 });
 
 PropertyValuesHolder smallerX = PropertyValuesHolder.ofFloat("scaleX", 2f, 1f);
 PropertyValuesHolder smallerY = PropertyValuesHolder.ofFloat("scaleY", 2f, 1f);
 ObjectAnimator smallerAnim = ObjectAnimator.ofPropertyValuesHolder( mHelloWorld, smallerX, smallerY);
 smallerAnim.setDuration(1000);
 smallerAnim.setStartDelay(1000); AnimatorSet animSet = new AnimatorSet();
 animSet.playSequentially(biggerAnim, smallerAnim);
 animSet.start(); 
  8. PropertyValuesHolder biggerX = PropertyValuesHolder.ofFloat("scaleX", 1f, 2f);
 PropertyValuesHolder biggerY = PropertyValuesHolder.ofFloat("scaleY",

    1f, 2f);
 ObjectAnimator biggerAnim = ObjectAnimator.ofPropertyValuesHolder( mHelloWorld, biggerX, biggerY);
 biggerAnim.setDuration(1000);
 biggerAnim.addListener(new Animator.AnimatorListener() {
 // onAnimationXXX
 });
 
 PropertyValuesHolder smallerX = PropertyValuesHolder.ofFloat("scaleX", 2f, 1f);
 PropertyValuesHolder smallerY = PropertyValuesHolder.ofFloat("scaleY", 2f, 1f);
 ObjectAnimator smallerAnim = ObjectAnimator.ofPropertyValuesHolder( mHelloWorld, smallerX, smallerY);
 smallerAnim.setDuration(1000);
 smallerAnim.setStartDelay(1000); AnimatorSet animSet = new AnimatorSet();
 animSet.playSequentially(biggerAnim, smallerAnim);
 animSet.start(); 
  9. PropertyValuesHolder biggerX = PropertyValuesHolder.ofFloat("scaleX", 1f, 2f);
 PropertyValuesHolder biggerY = PropertyValuesHolder.ofFloat("scaleY",

    1f, 2f);
 ObjectAnimator biggerAnim = ObjectAnimator.ofPropertyValuesHolder( mHelloWorld, biggerX, biggerY);
 biggerAnim.setDuration(1000);
 biggerAnim.addListener(new Animator.AnimatorListener() {
 // onAnimationXXX
 });
 
 PropertyValuesHolder smallerX = PropertyValuesHolder.ofFloat("scaleX", 2f, 1f);
 PropertyValuesHolder smallerY = PropertyValuesHolder.ofFloat("scaleY", 2f, 1f);
 ObjectAnimator smallerAnim = ObjectAnimator.ofPropertyValuesHolder( mHelloWorld, smallerX, smallerY);
 smallerAnim.setDuration(1000);
 smallerAnim.setStartDelay(1000); AnimatorSet animSet = new AnimatorSet();
 animSet.playSequentially(biggerAnim, smallerAnim);
 animSet.start(); 
  10. PropertyValuesHolder biggerX = PropertyValuesHolder.ofFloat("scaleX", 1f, 2f);
 PropertyValuesHolder biggerY = PropertyValuesHolder.ofFloat("scaleY",

    1f, 2f);
 ObjectAnimator biggerAnim = ObjectAnimator.ofPropertyValuesHolder( mHelloWorld, biggerX, biggerY);
 biggerAnim.setDuration(1000);
 biggerAnim.addListener(new Animator.AnimatorListener() {
 // onAnimationXXX
 });
 
 PropertyValuesHolder smallerX = PropertyValuesHolder.ofFloat("scaleX", 2f, 1f);
 PropertyValuesHolder smallerY = PropertyValuesHolder.ofFloat("scaleY", 2f, 1f);
 ObjectAnimator smallerAnim = ObjectAnimator.ofPropertyValuesHolder( mHelloWorld, smallerX, smallerY);
 smallerAnim.setDuration(1000);
 smallerAnim.setStartDelay(1000); AnimatorSet animSet = new AnimatorSet();
 animSet.playSequentially(biggerAnim, smallerAnim);
 animSet.start(); 
  11. What can do • PropertyValuesHolderがサポートしてる事 • 数値の変化 • scale, alpha,

    textSize, etc. 
  12. What can’t do • PropertyValuesHolderがサポートしてない事 • 数値以外の変化 • テキスト自体の操作 •

    PropertyValuesHolder#ofObjectで可能?? • 試した限りは出来なかった 12
  13. ScheduledExecutorService 

  14. ScheduledExecutorService • タスクを指定間隔で実行し続けるExecutors • Runnable / Callable • initialDelay •

    Period / Delay 
  15. ScheduledExecutorService • タスクを指定間隔で実行し続けるExecutors • Runnable / Callable • initialDelay •

    Period / Delay 
  16. Problem • 実行回数を指定できない • 少しずつ透明にしたい場合 • setAlpha(getAlpha() - 0.1)を10回 16

  17. FixedTimes ScheduledExecutorService  petitviolet/android-fixedtimes-ScheduledExecutorService

  18. FixedTimes ScheduledExecutorService • タスクを指定間隔で指定回数実行するExecutors • Runnable / Callable • initialDelay

    • Period / Delay • executeTime • onComplete callback 
  19. 

  20. final Runnable biggerTask = () -> {
 float scaleX =

    mTextView.getScaleX() + 0.05f;
 float scaleY = mTextView.getScaleY() + 0.05f;
 updateScale(mTextView, scaleX, scaleY);
 };
 final Runnable smallerTask = () -> {
 float scaleX = mTextView.getScaleX() - 0.05f;
 float scaleY = mTextView.getScaleY() - 0.05f;
 updateScale(mTextView, scaleX, scaleY);
 };
 updateScale(mTextView, 1f, 1f);
 mService.scheduleAtFixedRate(biggerTask, 20, 0, 50, TimeUnit.MILLISECONDS, () -> {
 showToast("reverse");
 mService.scheduleAtFixedRate(smallerTask, 20, 1000, 50, TimeUnit.MILLISECONDS, () -> {
 showToast("completed");
 updateScale(mTextView, 1f, 1f);
 });
 }); 
  21. final Runnable biggerTask = () -> {
 float scaleX =

    mTextView.getScaleX() + 0.05f;
 float scaleY = mTextView.getScaleY() + 0.05f;
 updateScale(mTextView, scaleX, scaleY);
 };
 final Runnable smallerTask = () -> {
 float scaleX = mTextView.getScaleX() - 0.05f;
 float scaleY = mTextView.getScaleY() - 0.05f;
 updateScale(mTextView, scaleX, scaleY);
 };
 updateScale(mTextView, 1f, 1f);
 mService.scheduleAtFixedRate(biggerTask, 20, 0, 50, TimeUnit.MILLISECONDS, () -> {
 showToast("reverse");
 mService.scheduleAtFixedRate(smallerTask, 20, 1000, 50, TimeUnit.MILLISECONDS, () -> {
 showToast("completed");
 updateScale(mTextView, 1f, 1f);
 });
 }); 
  22. final Runnable biggerTask = () -> {
 float scaleX =

    mTextView.getScaleX() + 0.05f;
 float scaleY = mTextView.getScaleY() + 0.05f;
 updateScale(mTextView, scaleX, scaleY);
 };
 final Runnable smallerTask = () -> {
 float scaleX = mTextView.getScaleX() - 0.05f;
 float scaleY = mTextView.getScaleY() - 0.05f;
 updateScale(mTextView, scaleX, scaleY);
 };
 updateScale(mTextView, 1f, 1f);
 mService.scheduleAtFixedRate(biggerTask, 20, 0, 50, TimeUnit.MILLISECONDS, () -> {
 showToast("reverse");
 mService.scheduleAtFixedRate(smallerTask, 20, 1000, 50, TimeUnit.MILLISECONDS, () -> {
 showToast("completed");
 updateScale(mTextView, 1f, 1f);
 });
 }); 
  23. final Runnable biggerTask = () -> {
 float scaleX =

    mTextView.getScaleX() + 0.05f;
 float scaleY = mTextView.getScaleY() + 0.05f;
 updateScale(mTextView, scaleX, scaleY);
 };
 final Runnable smallerTask = () -> {
 float scaleX = mTextView.getScaleX() - 0.05f;
 float scaleY = mTextView.getScaleY() - 0.05f;
 updateScale(mTextView, scaleX, scaleY);
 };
 updateScale(mTextView, 1f, 1f);
 mService.scheduleAtFixedRate(biggerTask, 20, 0, 50, TimeUnit.MILLISECONDS, () -> {
 showToast("reverse");
 mService.scheduleAtFixedRate(smallerTask, 20, 1000, 50, TimeUnit.MILLISECONDS, () -> {
 showToast("completed");
 updateScale(mTextView, 1f, 1f);
 });
 }); 
  24. 

  25. final Runnable biggerTask = () -> {
 String text =

    mTextView.getText().toString();
 updateText(mTextView, "x" + text + "x");
 };
 final Runnable smallerTask = () -> {
 String text = mTextView.getText().toString();
 updateText(mTextView, text.substring(1, text.length() - 1));
 };
 
 mService.scheduleAtFixedRate(biggerTask, 20, 0, 50, TimeUnit.MILLISECONDS, () -> {
 showToast("reverse");
 mService.scheduleAtFixedRate(smallerTask, 20, 1000, 50, TimeUnit.MILLISECONDS, () -> {
 showToast("completed");
 });
 }); 
  26. final Runnable biggerTask = () -> {
 String text =

    mTextView.getText().toString();
 updateText(mTextView, "x" + text + "x");
 };
 final Runnable smallerTask = () -> {
 String text = mTextView.getText().toString();
 updateText(mTextView, text.substring(1, text.length() - 1));
 };
 
 mService.scheduleAtFixedRate(biggerTask, 20, 0, 50, TimeUnit.MILLISECONDS, () -> {
 showToast("reverse");
 mService.scheduleAtFixedRate(smallerTask, 20, 1000, 50, TimeUnit.MILLISECONDS, () -> {
 showToast("completed");
 });
 }); 
  27. final Runnable biggerTask = () -> {
 String text =

    mTextView.getText().toString();
 updateText(mTextView, "x" + text + "x");
 };
 final Runnable smallerTask = () -> {
 String text = mTextView.getText().toString();
 updateText(mTextView, text.substring(1, text.length() - 1));
 };
 
 mService.scheduleAtFixedRate(biggerTask, 20, 0, 50, TimeUnit.MILLISECONDS, () -> {
 showToast("reverse");
 mService.scheduleAtFixedRate(smallerTask, 20, 1000, 50, TimeUnit.MILLISECONDS, () -> {
 showToast("completed");
 });
 }); 
  28. final Runnable biggerTask = () -> {
 String text =

    mTextView.getText().toString();
 updateText(mTextView, "x" + text + "x");
 };
 final Runnable smallerTask = () -> {
 String text = mTextView.getText().toString();
 updateText(mTextView, text.substring(1, text.length() - 1));
 };
 
 mService.scheduleAtFixedRate(biggerTask, 20, 0, 50, TimeUnit.MILLISECONDS, () -> {
 showToast("reverse");
 mService.scheduleAtFixedRate(smallerTask, 20, 1000, 50, TimeUnit.MILLISECONDS, () -> {
 showToast("completed");
 });
 }); 
  29. How to Implement • ScheduledExecutorService#schedule • 実行回数を保存、実行の度に++ • 指定回数に到達すると終了 •

    ScheduledFuture#cancel 
  30. これだけ 

  31. Pros • 自由 • そもそもアニメーションじゃなくても良い • ObjectAnimator知らなくて良い • 実行間隔を指定できる •

    レンダリング頻度の調節 
  32. Cons • Threadを余分に使う • Viewの更新ならUIスレッドへのpostが必要 • 地味な計算が必要 • scheduleWithFixedDelay /

    scheduleAtFixedRate • 複雑なアニメーションは辛い(?) • コードが汚そうに見える… 
  33.  Animator

  34.  FixedTimes ScheduledExecutorService

  35. Impression • 非同期でもアニメーション頑張れる • 意外と悪いことが無さそう • アニメーション以外でも使えて自由で良い • ほとんどのケースはObjectAnimatorで十分 •

    ObjectAnimator良く出来てる 
  36. Thanks!