【Android】マテリアルデザインのモーション実装カタログ

  • 121
    いいね
  • 0
    コメント

概要

カッコイイアニメーションをアプリで実装したいですよね?
その時にAndroidアプリを作っている時にエンジニアもデザイナもマテリアルデザインのアニメーションを実現できるのかよく分かっていない事が多いと思います。
エンジニアとしても実装できるのか分からず、また、デザイナーからもデザインとして上がってこないため実装しようとしません。またデザイナとしても無茶なお願いはしたくないので、エンジニアにお願いするのが忍びないというところがあるのではないかと思いました。
そこで実装カタログを作って、デザイナがこれを作れないかエンジニアに言い、それをエンジニアが見て実装する感じに出来たらいいなと思い書いてみました。

カタログへのまとめ方

日本語のマテリアルデザインガイドラインモーションに対しての実装方法を紹介します。(そこから引用させていただいています。)
ちなみに実装方法の方針としては、できるだけPlaidなどのGoogleのサンプルのやり方を取り入れて実装するという方法で行っています。
いくつかの実装がなかったり必要なさそうな部分は紹介していません。
またサードパーティのライブラリに依存せず、公式で、Canvasなどに手を入れたり大変な実装を行わない方向でまとめています。

カタログ

動的な継続時間 イージングカーブ
画像 image curve.gif
対応API Level※1 1 7(Support Library)
実装難易度※2
画面境界内での運動 矩形型変形
画像 tarnsform-rect.gif
API Level 21 11
実装難易度 ★★
放射型変形 分割と結合
画像 image image
API Level 21 ?
実装難易度 ★★★ 実装方法不明
連続性 作成
画像 image
API Level 21 7(Support Library)
実装難易度 ★★★
放射状の反応 アイコン イラスト
画像 image image irast.gif
API Level 21 11 (Support Library) -
実装難易度 ★★ ★★ (作るモノによる)

※1 サードパーティのライブラリに依存せず、公式で、Canvasなどに手を入れたり大変な実装を行わない場合のAPI Levelのものです。
APIレベルとAndroidのバージョン
7 = Android 2.1 Eclair MR1
11 = Android 3.0 Honeycomb
14 = Android 4.0 ICS
21 = Android 5.0 LOLLIPOP

※2 参考程度に。 ★3つでもだいたいの機能の実装よりは小さいはず、、です。。

継続時間とイージング

この章はアニメーションの継続時間とイージングについて書かれています。
基本的には認識できて、最速というところを目指していくと良いみたいです。
またイージング、動く速度の変化などは方法が提供されているので、それを利用しましょう。

動的な継続時間 一般的な継続時間

動的な継続時間、一般的な継続時間では、どのような時間でアニメーションさせるかが書かれています。

ガイドラインより

https://material.io/guidelines/motion/duration-easing.html

すべてのアニメーションの継続時間を同一にするのではなく、
移動距離、要素の速度、サーフェスの変化に合わせてアニメーションごとに継続時間を調整します。


モバイル
モバイル端末での移行は、次のような差異はありますが、一般に継続時間は300 ms を超えます。

* 大きく、複雑で、全画面の移行の場合は、継続時間を長めにする(375 ms を超えてもよい)
* 画面に入るオブジェクトの場合、継続時間は 225 ms を超える画面から出るオブジェクトの場合、継続時間は 195 ms を超える

400 ms を超える移行は遅すぎると思
われる可能性があります。

image

実装

アニメーションの時間を変えるために、setDurationなどで時間を調整します。
→ 175msから375msの間で動かすといいみたいです。 175ms=0dpの移動 375ms=500dp以上の移動みたいな感じで調整します。
ただこれは1オブジェクトが動く場合で、画面遷移などでいくつかのオブジェクが動いたりする場合は375msにしたりするみたいです。

image

自然なイージング カーブ

アニメーションの速度は一定ではなく、イージングカーブを利用して、変速させます。
標準カーブ、減速カーブ、加速カーブ、急カーブという種類があり、それぞれを場合によって使い分けて利用します。

標準カーブ

ガイドラインより

https://material.io/guidelines/motion/duration-easing.html#duration-easing-natural-easing-curves

最も一般的なイージング カーブです。
画面上の位置と位置の間で、オブジェクトはすぐに加速し、ゆっくりと減速します。
これは、プロパティ変更の中でも、拡大、縮小するマテリアルに適用されます。

curve.gif
image

画面境界内の 2 地点間の要素の運動は、自然な凹型の弧を描きます。
画面上の運動はすべて、標準カーブを使用します。

実装

→利用場所: 画面内の運動
→AndroidではFastOutSlowInInterpolatorを使う これはSupport Libraryにもあるのでそれを利用できる
https://developer.android.com/reference/android/support/v4/view/animation/FastOutSlowInInterpolator.html

実装例:

commentVotes.animate()
    .translationX(0f)
    .alpha(1f)
    .setDuration(200L)
    .setInterpolator(new FastOutSlowInInterpolator());

ちなみにアニメーションにはGingerbread(2.3) 以前をサポートする必要がなければAniamtor関係のクラスを使うと良いようです。(これはViewPropertyAnimatorを使う例です。)

減速カーブ

ガイドラインより

image

オブジェクトは画面の外からフルスピードで画面に入り、
停止点に向かってゆっくりと減速します。
減速時に、オブジェクトのサイズ(最大 100%)または不透明度(最大100%)を大きくしていくことができます。
オブジェクトが不透明度 0% で画面内に入る場合は、
画面に入る時点の大きいサイズから若干小さくすることもできます。

実装

利用場所: 画面に入る運動
速度変更のためにAndroidではLinearOutSlowInInterpolatorを使う こちらもSupport Libraryあり
https://developer.android.com/reference/android/support/v4/view/animation/LinearOutSlowInInterpolator.html
標準カーブの実装例のFastOutSlowInInterpolatorをLinearOutSlowInInterpolatorに置き換えるのみです。
画面内に入る時に透明度同時にalpha()を使えば透明度もアニメーションすることができます。

加速カーブ

ガイドラインより

image

オブジェクトはフルスピードで画面から出ます。画面から出るときは減速しません。
アニメーションの開始時に加速させ、サイズ(最小 0%)または不透明度(最小 0%)のいずれかを小さくしていくことができます。
オブジェクトが不透明度 0% で画面から出る場合は、サイズを若干大きくまたは小さくすることもできます。

実装

→利用場所: 画面から完全に出る運動
FastOutLinearInInterpolatorを使うことでできる
https://developer.android.com/reference/android/support/v4/view/animation/FastOutLinearInInterpolator.html

急カーブ

ガイドラインより

image

急カーブは、いつでも画面に戻ることができるオブジェクトに使用します。

オブジェクトは、画面上の開始点からすぐに加速させ、
その後、対照的なカーブで画面外の停止点に向けてすぐに減速させることができます。
正確なパスに沿って画面外の点に向かわないため、減速は標準カーブより高速になります。
オブジェクトはいつでもその点から戻ることができます。

実装

→利用場所: 画面から一時的に出る運動(ナビゲーションドロワーみたいなもの)
実装なし ただPathInterpolatorCompatを使えば実装できそうです。(PathInterpolatorCompat.create(0.4, 0, 0.6, 1) 検証できていないです)
https://developer.android.com/reference/android/support/v4/view/animation/PathInterpolatorCompat.html

運動

画面境界内での運動

画面境界内でアニメーションさせるときの方法について記述されています。

ガイドラインより

https://material.io/guidelines/motion/movement.html#movement-movement-within-screen-bounds

画面境界内の 2 地点間の要素の運動は、自然な凹型の弧を描きます。画面上の運動はすべて、
標準カーブを使用します。

実装

→丸くアニメーションさせる弧を用いた運動では、丸くアニメーションさせます(後述)
画面内の運動なので、標準カーブ(FastOutSlowInInterpolator)が利用されます。

ガイドラインより

https://material.io/guidelines/motion/movement.html#movement-movement-within-screen-bounds

現実の世界で重力に逆らって上昇するには、力が必要です。
画面上でも要素を上方向に移動するには、減速しながら上に向かうという運動を通して
加速中の力を表現する必要があります。

curve.gif

実装

[アニメーションの概要図]
image

TransitionアニメーションとPathMotionを使って実装します。
Support LibraryにもTransitionクラスはあるのですが、現状ではPathMotionに対応していないようなので、利用できません。

テーマにandroid:windowSharedElementEnterTransitionを設定する

    <style name="Plaid.Translucent.DesignerNewsStory">
...
        <item name="android:windowSharedElementEnterTransition">@transition/designer_news_story_shared_enter</item>
        <item name="android:windowSharedElementReturnTransition">@transition/designer_news_story_shared_return</item>

そのtransitionとして以下のようにセットする

<transitionSet
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="350"
    android:transitionOrdering="together">

    <transition
        class="android.transition.ChangeBounds"
        android:interpolator="@android:interpolator/fast_out_slow_in">
        <targets>
            <target android:targetId="@id/shot" />
        </targets>
    </transition>
    <pathMotion class="com.github.takahirom.plaidanimation.GravityArcMotion" />
</transitionSet>
ダメな例 :ng: 良い例 :ok:
output.gif output.gif
ArcMotionクラス GravityArcMotionクラス

pathMotionとして指定するものは標準のフレームワークのクラスであるArcMotionクラスが近いのですが、普通にやると不適切とされている動きをします。 | Googleのサンプル実装のPlaidにカスタマイズした実装があり、こちらはマテリアルデザインに沿った形でPathMotionクラスをカスタマイズしています。GravityArcMotionクラス これを使うことで以下のようにガイドラインに沿ったアニメーションが可能です。

実装例:
https://github.com/takahirom/PlaidAnimation/commit/8ad520bcd9ffa7f5bad8d9bd4727789aa1d46675

マテリアルの変形

マテリアルの形を変える時のガイドラインです。

矩形型変形

矩形型 つまり長方形の変形です。アニメーションを上下と左右でずらすことによってこれを実現します。

ガイドラインより

https://material.io/guidelines/motion/transforming-material.html#transforming-material-rectangular-transformation

tarnsform-rect.gif

実装

→ CardView + アニメーションの幾つかの方法で実装可能
例:

scale.gif
ViewPropertyAnimatorによる方法

final CardView cardView = (CardView) findViewById(R.id.card);
cardView.setOnClickListener(new View.OnClickListener() {
    boolean isSmall = true;
    @Override
    public void onClick(View view) {
        if (isSmall) {
            // 大きくする
            ViewCompat
                    .animate(view)
                    .scaleX(3.0f)
                    .setDuration(275)
                    .setStartDelay(0)
                    .setInterpolator(new FastOutSlowInInterpolator())
                    .start();
            ViewCompat
                    .animate(view)
                    .scaleY(3.0f)
                    .setDuration(350)
                    .setStartDelay(25) // 開始をずらす
                    .setInterpolator(new FastOutSlowInInterpolator())
                    .start();
        } else {
            // 小さくする
            ViewCompat
                    .animate(view)
                    .scaleX(1.0f)
                    .setDuration(325)
                    .setStartDelay(50)
                    .setInterpolator(new FastOutSlowInInterpolator())
                    .start();
            ViewCompat
                    .animate(view)
                    .scaleY(1.0f)
                    .setStartDelay(0)
                    .setDuration(325)
                    .setInterpolator(new FastOutSlowInInterpolator())
                    .start();
        }
        isSmall = !isSmall;
    }
});

放射型変形

丸く広がるアニメーションです。FloatingActionButton (FAB)を変形させて、カードにしたりする場合に利用されます。

ガイドラインより

https://material.io/guidelines/motion/transforming-material.html#transforming-material-radial-transformation
transform-fab.gif

実装

output.gif

SharedElementTransitionを利用して以下のようにコーディングします。
image

カスタムTransition

カスタムTransitionとして指定しているFabTransformクラスは以下のように実装してあげることであのようなアニメーションができます。

https://github.com/nickbutcher/plaid/blob/master/app/src/main/java/io/plaidapp/ui/transitions/FabTransform.java

注目するべきはTransitionクラスで実装する必要があるcreateAnimatorメソッドです。開始時の位置が取得できるTransitionValues startValuesと終了時の位置とViewが取得できるTransitionValues endValuesが引数として渡ってくるので、自分でアニメーションさせるAnimatorを作成します。

@Override
public Animator createAnimator(final ViewGroup sceneRoot,
                               final TransitionValues startValues,
                               final TransitionValues endValues) {
    // 開始場所の取得
    final Rect startBounds = (Rect) startValues.values.get(PROP_BOUNDS);
    // 終了場所の取得
    final Rect endBounds = (Rect) endValues.values.get(PROP_BOUNDS);
    // アニメーションするViewの取得
    final View view = endValues.view;

まず円形に広がるAnimatorであるCircularRevealを作成します。
ViewAnimationUtils.createCircularRevealにより、始まりのx、yと、終わりの大きさを指定して作成します。

// Circular clip from/to the FAB size
final Animator circularReveal;
if (fromFab) {
    circularReveal = ViewAnimationUtils.createCircularReveal(view,
            view.getWidth() / 2,
            view.getHeight() / 2,
            startBounds.width() / 2,
            (float) Math.hypot(endBounds.width() / 2, endBounds.height() / 2));
    circularReveal.setInterpolator(
            new FastOutLinearInInterpolator());

最終的に円形で広がるcircularRevealと位置を変更するtranslateと色を変更するcolorFadeと元のiconをフェードアウトするiconFadeを同時に動かしています。

final AnimatorSet transition = new AnimatorSet();
transition.playTogether(circularReveal, translate, colorFade, iconFade);
transition.playTogether(fadeContents);

https://github.com/takahirom/PlaidAnimation/commit/4159feaf610203c84c5e06be0fa1edd59d10818c

分割と結合

マテリアルデザインの動画でも出てくるマテリアルの結合と分割です。

ガイドラインより

https://material.io/guidelines/motion/transforming-material.html#transforming-material-joining-dividing
material-devide.gif

結合
マテリアルを他のマテリアルと結合させたり、複数のパーツに分割したりできます。
マテリアルの 2 つのパーツが互いに接近しながら、
縁同士がぶつかり、マージンが重なって、運動が完了します。
分割
マテリアルを複数のパーツに分割する場合は、動き始めた時点でパーツが分離し始めます。
シャドウ
切り離されたマテリアルのシャドウは、兄弟要素の上には表示されません。

実装

正直わからないです。 :bow:
どなたか分かる方いたら教えてください :sos:
ただのCardであれば1つのCardViewを4つのCardViewにして、ViewPropertyAnimatiorなどで離すのみですが、、コンテンツもあると実装苦しそうです
RecyclerViewなどでもできそうですがどうでしょうか。実装例をご存じの方はお教えください。

コレオグラフィ

コレオグラフィの意味よくわからなかったのですが、マテリアルデザインにおける “Choreography“(振り付け)を知ろうという記事があり「ユーザーを適切な場所へ誘導するための手法」ということだそうです。

連続性

一つの同じオブジェクトを保持したまま遷移後も利用する連続性です。

ガイドライン

https://material.io/guidelines/motion/choreography.html#choreography-continuity

コンテンツのすべての要素が共有される場合サーフェスを展開するとき、
遷移中にかなりの数の要素を表示し続ける必要があります。
複雑な遷移では、1 つの要素を常に見えるようにしてください(下のアニメーションをご覧ください)。

continuity.gif

実装

こちらについてはこれまで紹介しているSharedElementTransitionで実装可能となります。
放射型変形と同じように細かい調整のためにカスタムTransitionを使う必要があると思われます。

作成

新しいマテリアルを作成するときに利用するものです。メニューボタンなどで利用することがあると思います。

ガイドライン

https://material.io/guidelines/motion/choreography.html#choreography-creation
creation_guideline.gif

実装

android.support.v7.widget.ListPopupWindowクラスを使うことで、近いものはかんたんに実現できます。
一応こちらでもユーザーの視線を誘導することはできているので目的は果たせそうです。
ガイドラインのようにリストを上に出すために、ちょっとしたハックをしていますが、基本的に必要ないはずです。

final ListPopupWindow listPopupWindow = new ListPopupWindow(MainActivity.this);
final ArrayAdapter<String> adapter = new ArrayAdapter<>(MainActivity.this, R.layout.support_simple_spinner_dropdown_item);
adapter.add("test");
adapter.add("test2");
listPopupWindow.setWidth(dip2px(200));
listPopupWindow.setAdapter(adapter);
listPopupWindow.setAnchorView(view);

// show top of anchor view
// FIXME: ugly hack
int[] attribute = new int[] { R.attr.dropdownListPreferredItemHeight };
TypedArray array = obtainStyledAttributes(0, attribute);
int rowHeight = array.getDimensionPixelSize(0, -1);
listPopupWindow.setVerticalOffset(-view.getHeight() - rowHeight * adapter.getCount());

listPopupWindow.show();

creation.gif
表示途中でどういう変形をしているか分かる画像
image

ガイドラインより

https://material.io/guidelines/motion/choreography.html#choreography-creation

creation-list.gif

実装

nissiyさんが書いている『OSSから学ぶActivity起動時のカッコいいアニメーション』の『ふわっと表示される各写真』にあるようにRecyclerViewのAdapterのonBindViewHolderでやるのが良さそうです。
http://qiita.com/nissiy/items/9b978ed1925f3605a25c#%E3%81%B5%E3%82%8F%E3%81%A3%E3%81%A8%E8%A1%A8%E7%A4%BA%E3%81%95%E3%82%8C%E3%82%8B%E5%90%84%E5%86%99%E7%9C%9F

斜めのときのやり方は行数と列数足し算して、setStartDelayを設定するような感じでできそうです。

放射状の反応

タッチした場所から丸く広がるアニメーションです。

ガイドラインより

https://material.io/guidelines/motion/choreography.html#choreography-radial-reaction

circle.gif

実装

放射型変形と同じようにViewAnimationUtils.createCircularRevealを使い、タップされた場所から広がるように引数に座標を渡せばできそうです(未実装)

クリエイティブなカスタマイズ

アイコンをアニメーションさせたり、何もない状態のときに楽しい画像を出したりというところです。

アイコン

アイコンのアニメーションは2つの機能をトグルするような使い方が主な使い方のようです。

ガイドラインより

https://material.io/guidelines/motion/creative-customization.html#creative-customization-icons

1 つのアイコンで 2 つの機能を果たすことができます。
たとえば、メニューアイコンが再生コントロールにスムーズに移行し、
再びメニューに戻ることができれば、
ユーザーにボタンの機能を伝えると同時に、
操作に驚きの要素を加味できます。

icon.gif

実装

回転と移動程度であればAnimatedVectorDrawableCompatにより実現可能です。
AnimatedVectorDrawableCompatの作成方法についてはAndroidIconAnimatorというツールを使うことにより割と簡単に作ることができます。
ちなみにメニューが戻るボタンになったりするのは、サンプルにあるので、それを参考に作ってみると良いと思われます。
vector.gif

https://speakerdeck.com/takahirom/support-vector-drawable
https://romannurik.github.io/AndroidIconAnimator/
アニメーションアイコンが作れるAndroid Icon Animatorを触ってみる
http://qiita.com/takahirom/items/3deee8b73e0a2bc1a50b
Android Icon Animatorを活用してアニメーションリソース軽量化
http://tech.vasily.jp/entry/android_icon_animator_tutorial

イラスト

何もない状態のときにアニメーションさせたりというところです。

ガイドラインより

https://material.io/guidelines/motion/creative-customization.html#creative-customization-illustrations

画像やイラストのわずかなアニ
メーションで、ユーザー エク
スペリエンスに楽しさを加える
ことができます。

irast.gif

実装

このアニメーションはAnimatedVectorDrawableでもいいですし、Glideなどを利用して、gif画像を利用してもいいと思います。

下位互換性はどうするか?

API Level 21未満のユーザーを現状では切り捨てることはだいぶ難しいです。
Support Libraryは下位互換性を保持してくれるライブラリですが、現状はサポートしきれていない部分があります。
そこで2つのアプローチがあります。

アプローチ1 下位バージョンではアニメーションを諦める

今からスマホにアプリ入れて来ている新規のユーザーは結構5.0以上が多いです。
公式のDashboardsより、新規だけでなく60%以上のユーザーがAndroid 5.0以上を使っています。
image

なので、その新規のユーザーにフォーカスして諦める。というのも良いと思います。
そして、Support Libraryの対応を待つというアプローチです。

コード例

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
    // Android 5.0以上のユーザーだけここでアニメーション
}

アプローチ2 頑張って実装する

外部のライブラリなどを用いたり、自分で頑張って行います。
すべてのユーザーにアニメーションの体験が提供できるので嬉しいです。
ただ公式の方法とは異なるので、微妙に動きが違ったり、サポートを受けにくくなったりなどデメリットがあるので、考えて行う必要があります。

終わりに

マテリアルデザインは結構浸透していますが、モーションは中々実践できていない部分ではないかと思います。下位バージョンを除いてモーションは実装する方法は一通りだいたい用意されているようです。
ただ、マテリアルデザインのモーションはこれで一通りみたのですが、Patternsなどに基礎を応用したさまざまなアニメーションがあるのですが、そこについては説明できていません。しかし、だいたいは何かしらやり方はあるはずなので、調べてみてください。
ガイドラインにある重要性、目的を意識しつつ、採用していければ、ユーザーが迷わなかったり、待ち時間を意識せずに利用できたりなどをしていけるようになるはずです。
モダンでイケイケなアプリを作っていきましょう!

マテリアルモーションのモーションの重要性より


image