$30 off During Our Annual Pro Sale. View Details »

Android Animation by ScheduledExecutorService

petitviolet
September 29, 2015

Android Animation by ScheduledExecutorService

petitviolet

September 29, 2015
Tweet

More Decks by petitviolet

Other Decks in Technology

Transcript

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

    View Slide

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

    View Slide

  3. アニメーションしてますか

    View Slide


  4. View Slide

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

    View Slide


  6. View Slide

  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();

    View Slide

  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();

    View Slide

  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();

    View Slide

  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();

    View Slide

  11. What can do
    • PropertyValuesHolderがサポートしてる事
    • 数値の変化
    • scale, alpha, textSize, etc.

    View Slide

  12. What can’t do
    • PropertyValuesHolderがサポートしてない事
    • 数値以外の変化
    • テキスト自体の操作
    • PropertyValuesHolder#ofObjectで可能??
    • 試した限りは出来なかった
    12

    View Slide

  13. ScheduledExecutorService

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  17. FixedTimes
    ScheduledExecutorService

    petitviolet/android-fixedtimes-ScheduledExecutorService

    View Slide

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

    View Slide


  19. View Slide

  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);

    });

    });

    View Slide

  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);

    });

    });

    View Slide

  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);

    });

    });

    View Slide

  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);

    });

    });

    View Slide


  24. View Slide

  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");

    });

    });

    View Slide

  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");

    });

    });

    View Slide

  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");

    });

    });

    View Slide

  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");

    });

    });

    View Slide

  29. How to Implement
    • ScheduledExecutorService#schedule
    • 実行回数を保存、実行の度に++
    • 指定回数に到達すると終了
    • ScheduledFuture#cancel

    View Slide

  30. これだけ

    View Slide

  31. Pros
    • 自由
    • そもそもアニメーションじゃなくても良い
    • ObjectAnimator知らなくて良い
    • 実行間隔を指定できる
    • レンダリング頻度の調節

    View Slide

  32. Cons
    • Threadを余分に使う
    • Viewの更新ならUIスレッドへのpostが必要
    • 地味な計算が必要
    • scheduleWithFixedDelay / scheduleAtFixedRate
    • 複雑なアニメーションは辛い(?)
    • コードが汚そうに見える…

    View Slide


  33. Animator

    View Slide


  34. FixedTimes
    ScheduledExecutorService

    View Slide

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

    View Slide

  36. Thanks!

    View Slide