Slide 1

Slide 1 text

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

Slide 2

Slide 2 text

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

Slide 3

Slide 3 text

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

Slide 4

Slide 4 text

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

ScheduledExecutorService

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

FixedTimes ScheduledExecutorService petitviolet/android-fixedtimes-ScheduledExecutorService

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

Slide 20

Slide 20 text

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

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

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

Slide 23

Slide 23 text

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

Slide 24

Slide 24 text

Slide 25

Slide 25 text

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

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

これだけ

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

Animator

Slide 34

Slide 34 text

FixedTimes ScheduledExecutorService

Slide 35

Slide 35 text

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

Slide 36

Slide 36 text

Thanks!