class: chapter-1, hero, center, middle #
動かす
DevFest Kansai 2016 2016/11/27 荒木佑一 --- class: chapter-1, normal # 自己紹介 .card[ 荒木佑一 [@yuichi_araki](https://twitter.com/yuichi_araki) Developer Programs Engineer @Google - サポート ライブラリ (主に design と transition) - Google I/O アプリ - Google Santa Tracker アプリ - [d.android.com/samples](http://d.android.com/samples) のサンプルいろいろ - [CameraView](https://github.com/google/cameraview) ] --- class: chapter-1, normal # 意味のある動き .card[ ![Meaningful Motion](materialdesign_principles_motion.png) マテリアル デザイン [Choreography](https://material.google.com/motion/choreography.html) (振り付け) - ユーザーが操作を行う - 画面表示が変わる 両者の関係性を明瞭にするためアニメーションが有効 ] ??? - Android アプリにおけるアニメーションの重要性が高まっている --- class: chapter-1, normal # 本日の話題 .card[ - Animation - ViewSwitcher - Activity/Fragment のアニメーション - Animator - ViewPropertyAnimator - animateLayoutChanges - Transition サンプル: https://goo.gl/zaR6H2 ] ??? - Android で利用できるアニメーション関連機能をざっとおさらい - 以下については本日は話しません - Drawable のアニメーション (VectorDrawableCompat, AnimationDrawable) - Shared Element Transition (Activity/Fragment Transition) --- class: chapter-2, hero, center, middle # Animation と Animator --- class: normal, chapter-2 # Animation と Animator の共通点 .card[ View (など) を動かす コード (Java) またはリソース (XML) で定義・生成できる リスナーを設定できる AnimationSet/AnimatorSet で複数組み合わすことができる Interpolator を設定できる ] --- class: chapter-2, normal # 相違点 1: API レベル .card[ ## Animation 1 ## Animator 11 (Honeycomb) ] ??? - ちなみに Android のバージョンに名前が付いたのは Android 1.5 Cupcake (API レベル 3) が初めて - API レベル 1 と 2 に名前はない --- class: chapter-2, normal # 相違点 2: 動かす対象 .card[ ## Animation View ## Animator 何でも ] ??? - Animation は View Animation とも呼ばれる --- class: chapter-2, normal # 相違点 3: 動かす属性 .card[ ## Animation - 平行移動 (translate) - 回転 (rotate) - 透過 (alpha) - 拡大縮小 (scale) ## Animator 対象に setter があれば何でも ] --- class: chapter-2, normal # 使い分けの方針 .card[ ## Animation - Gingerbread 以前をサポートする必要があるとき ## Animator - 上記以外 - こちらを基本としたい ] --- class: chapter-3, hero, center, middle # Animation --- class: chapter-3, normal # Animation の使い方 .level[1] .card[ res/anim/translate.xml ```xml <translate xmlns:android="http://schemas.android.com/apk/res/android" android:duration="@integer/duration_long" android:fromXDelta="0" android:toXDelta="500"/> ``` ```java View target = ...; Animation translate = AnimationUtils.loadAnimation(context, R.anim.translate); target.startAnimation(translate); ``` ] ??? - 平行移動の例 - XML で Animation を定義してコードから利用している - コードで Animation を生成することもできる - Gingerbread 向けでない限り直接使うことはもうあまりないのでは - Animation は View を動かしたあと元の状態に戻す --- class: chapter-4, hero, middle, center # ViewSwitcher --- class: chapter-4, normal # ViewSwitcher .level[1] .card[ Animation に基づいた高レベル機能 FrameLayout を継承した ViewGroup - ViewFlipper - ViewSwitcher - ImageSwitcher - TextSwitcher ] ??? --- class: chapter-4, normal # 例: TextSwitcher .level[1] .card[ ```java TextSwitcher mTextSwitcher; mTextSwitcher = (TextSwitcher) view.findViewById(R.id.text); mTextSwitcher.setFactory(new ViewSwitcher.ViewFactory() { @Override public View makeView() { // TextView を生成 return LayoutInflater.from(MainActivity.this) .inflate(R.layout.switcher_text, mTextSwitcher, false); } }); mTextSwitcher.setText("Hello"); // 始めはアニメーションなし mTextSwitcher.setInAnimation(this, R.anim.text_in); mTextSwitcher.setOutAnimation(this, R.anim.text_out); ``` ```java mTextSwitcher.setText("Bonjour"); // アニメーション付きで変更 ``` ] ??? - 例は TextSwitcher だが、ImageSwitcher など他の仲間もだいたい使い方は同じ - 最初に ViewFactory を設定する必要がある - この場合 TextSwitcher なので TextView を生成する - テキストが表示されるときと消えるときのアニメーションを設定する - 何かが変化したことをわかりやすくできる - リアルタイムでテキストが変わっていくカウントダウンなど - ロード中インジケーターとコンテンツの切り替え --- class: chapter-5, hero, middle, center #
Activity/Fragment の
アニメーション
--- class: chapter-5, normal # Activity のアニメーション .level[5] .card[ ```java @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_sample); // 起動時のアニメーション `overridePendingTransition`( R.anim.slide_in_end, R.anim.slide_out_start); } ``` ```java @Override public void finish() { super.finish(); // 終了時のアニメーション `overridePendingTransition`( R.anim.slide_in_start, R.anim.slide_out_end); } ``` ] ??? - Activity が起動するときと終了するときのアニメーションを指定できる - デフォルトのアニメーションから変更したいときに --- class: chapter-5, normal # Fragment のアニメーション .level[9] .card[ Fragment 表示・非表示の際のアニメーションを指定できる ```java getSupportFragmentManager().beginTransaction() .`setCustomAnimations`( R.anim.slide_in_end, R.anim.slide_out_start, R.anim.slide_in_start, R.anim.slide_out_end) .addToBackStack(null) .replace(R.id.container, sample.createFragment()) .commit(); ``` ] ??? - Animation まとめ - Animation 自体をそのまま使うことはあまりないはずだが、一部の高レベル機能はまだ有用 - 使わないでいいなら使わない方が学習コスト的にいいのでは --- class: chapter-6, hero, center, middle # Animator --- class: chapter-6, normal # Animator .card[ ## ValueAnimator 何かの値を変化させる ## ObjectAnimator (extends ValueAnimator) 変化した値を対象にセットする ## AnimatorSet 複数の Animator を組み合わせる ] --- class: chapter-2, normal # 名前の罠 .card[ これらは **Animator ではない** (継承していない) - ViewAnimator - ViewSwitcher の親クラス - AdapterViewAnimator - StackView の親クラス - ViewPropertyAnimator - 簡易アニメーション (内部で Animator を利用) - StateListAnimator - Drawable の状態変化アニメーション (内部で Animator を利用) ] --- class: chapter-6, normal # ValueAnimator .level[11] .card[ 値を変化させるだけのシンプルな Animator ```java Animator animator = ValueAnimator .ofInt(0, 100) // 範囲 .setDuration(5000); // ミリ秒 animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator a) { int value = (int) `a.getAnimatedValue()`; // … value を使って何かする } }); animator.start(); ``` ] ??? - コードでも XML でも生成できる - XML で作るなら <animator> タグ - シンプル - 設定した型で設定した範囲の値が返ってくるだけ - デフォルトでサポートされている型は int, float, color - 返ってきた値で何をするかは利用者次第 --- class: chapter-6, normal # ValueAnimator (XML) .level[11] .card[ ```xml <animator xmlns:android="http://schemas.android.com/apk/res/android" android:valueFrom="0" android:valueTo="100" android:valueType="intType" android:duration="5000"/> ``` ```java ValueAnimator animator = (ValueAnimator) AnimatorInflater .loadAnimator(context, R.animator.count_up); // AnimatorUpdateListener を設定 animator.start(); ``` ] --- class: chapter-6, normal # ObjectAnimator .level[11] .card[ ValueAnimator の機能に加えて - 対象 Target に値を設定できる - セッター (set なんとか) が必要 - 開始値を自動的に取得する - ゲッター (get なんとか) が必要 ] --- class: chapter-6, normal # View をアニメーション .level[11] .card[ ```java View target = view.findViewById(R.id.target); ObjectAnimator.ofFloat(target, "translationX", 500.f) .setDuration(1000) .start(); ``` ] ??? - View のプロパティー、ここでは translationX をアニメーションさせている - "translationX" は文字列で指定されている - ObjectAnimator はどうやってプロパティーを View にセットしているのか --- class: chapter-6, normal # 何でもアニメーション .level[11] .card[ ```java public class Target { private int mValue; Target(int value) { mValue = value; } void `setValue`(int value) { mValue = value; } int `getValue`() { return mValue; } } ``` ```java Target target = new Target(10); ObjectAnimator .ofInt(target, "value", 110) .setDuration(5000) .start(); ``` ] ??? - ObjectAnimator は何でもアニメーションできる - カスタム View でカスタムのゲッター・セッターがあれば、それをアニメーションさせることもできる --- class: chapter-6, normal # ObjectAnimator (XML) .level[11] .card[ ```xml <objectAnimator xmlns:android="http://schemas.android.com/apk/res/android" android:propertyName="translationX" android:valueTo="500" android:duration="1000" android:valueType="floatType"/> ``` ```java Animator animator = AnimatorInflater .loadAnimator(getContext(), R.animator.slide); animator.setTarget(view); animator.start(); ``` ] --- class: chapter-6, normal # ValueAnimator と ObjectAnimator .card[ ## ValueAnimator - 値を変化させるだけ - リスナーで変化した値を取得して利用 - 初期値指定 必須 ## ObjectAnimator - 変化した値を対象にセットする - 変化した値は対象のセッターでセット - 初期値指定 任意 - 省略した場合は対象のゲッターで現在値を取得 ] --- class: chapter-6, normal # AnimatorSet .level[11] .card[ ```java AnimatorSet set = new AnimatorSet(); set.playTogether( ObjectAnimator.ofFloat(view1, "translationX", 500.f), ObjectAnimator.ofPropertyValuesHolder(view2, PropertyValuesHolder.ofFloat("alpha", 0.f), PropertyValuesHolder.ofFloat("translationY", 100.f))); set.setDuration(1000); set.start(); ``` 複数の Animator を同時・逐次再生できる 対象が同じなら ObjectAnimator と PropertyValuesHolder を組み合わせることもできる ] --- class: chapter-7, hero, middle, center # ViewPropertyAnimator --- class: chapter-7, normal # ViewPropertyAnimator .level[12 (9)] .card[ Animator を継承して**いない** (内部では Animator を使っている) ```java target.animate().translationX(500.f).alpha(0.f); ``` または ```java ViewCompat.animate(target).translationX(500.f).alpha(0.f); ``` .start() がなくても開始する ] ??? - ViewCompat を使っても古いバージョンでアニメーションが起こるわけではない - start() があれば即時開始 - ValueAnimator や ObjectAnimator の場合 start() がないと開始しないので --- class: chapter-7, normal # ViewPropertyAnimator vs ObjectAnimator .card[ ## ViewPropertyAnimator - 簡単、型安全 - AnimatorSet で組み合わせられない - 勝手に始まる ## ObjectAnimator - View でなくても使える柔軟性 - やや冗長 ] ??? - 使いやすいからといって何でも ViewPropertyAnimator にするのは逆に大変 - ある程度複雑なことは ObjectAnimator や ValueAnimator の仕様を検討する --- class: chapter-8, hero, middle, center # animateLayoutChanges --- class: chapter-8, normal # animateLayoutChanges .level[11] .card[ ViewGroup の中のレイアウト変更をアニメーション ```xml <LinearLayout android:id="@+id/box" android:layout_width="match_parent" android:layout_height="match_parent" `android:animateLayoutChanges="true"` android:orientation="vertical"/> ``` ```java // 追加 mBox.addView(view); // 削除 mBox.removeView(view); ``` ] ??? - どんな ViewGroup でも使える - XML の属性だけなら古い API レベルでも無視されるだけ --- class: chapter-9, hero, middle, center # Transition --- class: chapter-9, normal # Transition .level[14] .card[ レイアウトの変更を自動的にアニメーションする .large[ ![Transition](transition.png) ] サポートライブラリで 14 までバックポート ] --- class: chapter-9, normal # setVisibility をアニメーション .card[ View の visibility を VISIBLE と GONE で切り替える ```java mTarget.setVisibility( mTarget.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE); ``` - VISIBLE → GONE - GONE → VISIBLE - アニメーションの途中で切り替えたら? - その View 以外にも View があったら? ] ??? - VISIBLE → GONE: アニメーションで alpha を 0 に。アニメーションが完了したら setVisibility(GONE) するリスナーをセットしておく。 - GONE → VISIBLE: 先に setVisibility(VISIBLE) する。その際 alpha は 0 で。アニメーションで alpha を 1 にする。 - 途中で切り替えても大丈夫にするにはアニメーションのオブジェクトを保持しておいて、現在アニメーション中か判別する必要がある - 開始する alpha の値は現在の alpha でなくてはいけない - 他の View を移動するアニメーションも行う必要がある --- class: chapter-9, normal # beginDelayedTransition .level[14] .card[ ```java * TransitionManager.beginDelayedTransition(mRoot); mTarget.setVisibility( mTarget.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE); ``` デフォルト (AutoTransition) で対応しているもの - アニメーション前後で存在の有無が違う View はフェードイン・フェードアウト - 位置・サイズが違う View は平行移動・サイズ変更 ] ??? - この一行だけでアニメーションができる - 他の例 - View の LayoutParams を書き換えて左寄せと右寄せを切り替える - LinearLayout の先頭の View が GONE になって後ろの View がずれる --- class: chapter-9, normal # カスタム Transition .level[14] .card[ アニメーション前後で、必要な View の属性を取得する - void captureStartValues(TransitionValues) - void captureEndValues(TransitionValues) 取得した値にしたがって Animator を生成する - Animator createAnimator(ViewGroup, TransitionValues, TransitionValues) ```java TransitionManager.beginDelayedTransition(mRoot, `new MyTransition()`); ``` ] --- class: chapter-16, normal # まとめ .card[ 適切な機能を使う - animateLayoutChanges - Transition ] ??? 何でも View.animate で解決しようとしない --- class: chapter-17, hero # Android プラットフォーム # サポート ライブラリ ## b.android.com ### バグ報告、機能要望はこちらから ### ※ スクリーンショット (動画) 重要 ??? - Animator: start() で開始 - ViewPropertyAnimator: start() しなくても開始