既存のアプリを Material Design 化して得た知見

当記事は [Android Advent Calendar 2015](http://qiita.com/advent-calendar/2015/android) の 18 日目の記事です。

つい最近のことなのですが、既にリリースされているアプリを Material Design 化するお仕事に携われたので、そこで得た知見を書きます。少しでも参考になれば幸いです。

## tl;dr

* デザイナーもエンジニアも Material Design ガイドラインを読もう
* zeplin が便利だった
* Activity と Fragment のどちらで Toolbar を持つかについては... まだ決定的な理由はわからない
* カスタムフォントに Noto font 使うとデザイン崩れが起きる
* 既存アプリの Material Design 化は段階的にしよう

## Material Design ガイドラインを読もう

余白とかアニメーションとか影など、具体的な dp 値とかルールとかそういうのは **「Material Design ガイドラインを読む」** としか言えないのでどんどん読みましょう。

お仕事で一緒のチームになったデザイナーさんは **Material Design 勉強していくぞ！！！** と、かなりガイドラインを読み込んで勢いある感じで、 **「デザイナーもエンジニアもアプリのデザインについて一緒にあーだこーだ言ってこ↑↑↑↑↑↑↑↑」** 感が大切だということを非常に感じました。開発者目線として普段からアプリを触っているエンジニアさんならではの目の付け所があると思うので、デザイナーさんと一緒にデザインを考えていくべきだと思います。

そこで便利なのが、次項で紹介する zeplin というツールです。zeplin を使うと、デザイナーとエンジニアのコミュニケーションが割とスムーズに進みました。

## zeplin が便利だった

今回、アプリの画面のデザイン仕様は zeplin というツールを使って決めました。

https://zeplin.io/

![zeplin__1.png](https://qiita-image-store.s3.amazonaws.com/0/35834/61b3f57d-a561-beb2-e18d-99d9b059a5ea.png)

各画面の遷移図などは書くことができませんでしたが、

* 画面上にコメントができ、そのコメントに reply が可能
* Android 特有の dp 単位に対応している
* 各 View 間の margin も見れる
* カスタムフォントを設定しておけば、そのフォントで文字が描画される

などなど、グラフィカルにデザイナーさんとのコミュニケーションが可能で非常に便利なので、導入すると有用なコミュニケーションツールとして発揮するのではと感じました。


## Activity と Fragment のどちらで Toolbar を持つか

当項目は、Material Design 自体のノウハウではないような気がしますが、悩んだところではあるので書いています。

今回の Material Design 化において、Toolbar を採用しました。

理由は、

* アプリのメニューを表現するために [Navigation drawer](http://www.google.com/design/spec/patterns/navigation-drawer.html) パターンを採用する
* その Navigation drawer はアクションバーの上に重なるように開閉する
* 既存の ActionBar を使用すると、Navigation drawer がアクションバーに重ならない
* なので、既存の ActionBar の代替となる Toolbar を採用

です。

![patterns_navdrawer_elevation1.png](https://qiita-image-store.s3.amazonaws.com/0/35834/e187029e-5d8f-9a17-dc16-21229cdfdb19.png)

そこで出てきた問題が、 **Activity と Fragment のどちらが Toolbar を保持するべきか？** というものです。 Toolbar は ViewGroup を継承したただの View なため、ActionBar とは違い Fragment にも持たせられるためがゆえに出てくる問題でした。

結論として、 **Activitiy で保持する** ようにしました。以下に理由を述べます。

### 同階層の画面間での画面遷移アニメーション

同階層間の画面遷移アニメーションについて考えると、Activity が Toolbar を保持したほうが都合がよいと思いました。

今回改修したアプリの画面階層は、ざっくり言うと以下の通りで、階層を Activity で切り分け、階層内の画面を Fragment で切り分けしています。各階層間・画面間は画面遷移が可能です。

```
[第 1 階層：MainActivity]
　├ A 画面 (Fragment)
　├ B 画面 (Fragment)
　├ C 画面 (Fragment)
　└ D 画面 (Fragment)

[第 2 階層：SubActivity]
　└ A' 画面 (Fragment)
```

ここで、階層内の画面間の遷移アニメーションを以下の２パターンで見てみます。 

1. Activity が Toolbar を持っている場合
2. Fragment が Toolbar を持っている場合

> ここでのアニメーションは `FragmentTransaction#setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN)` を用いてます

|Activity が Toolbar を保持|Fragment が Toolbar を保持|
|:---------------:|:---------------:|
|![transition_activity_have_toolbar.gif](https://qiita-image-store.s3.amazonaws.com/0/35834/f3a50c0a-c840-a688-7145-e959c77e0490.gif)|![transition_fragment_have_toolbar.gif](https://qiita-image-store.s3.amazonaws.com/0/35834/195a7197-ea0d-b908-26d3-1896178e3bb7.gif)
|

当然ですが、Fragment が Toolbar を保持すると **Toolbar も一緒に** 遷移アニメーションします。なので、実際は同じ階層内の画面間で遷移しているにも関わらず **別階層に遷移した** ように見えてます。

ですが、Activityが Toolbar を保持している場合の遷移アニメーションのほうが、 **「各画面の共通オブジェクトとして Toolbar が残って見えるので、 """同階層""" である」** というのが自然に感じ取れることがわかります。

なので、 Toolbar は Activity で保持するように決めました。

> 「本来の Material Design における画面遷移は [コレ](http://www.google.com/design/spec/animation/meaningful-transitions.html) に沿うべき」かと思いましたが、工数が...って感じで妥協しました。ただ、そのまえに google は実装サンプルをどんどん展開してくれ頼む！！！！！！って感じですが...

### CoordinatorLayout, AppbarLayout と併用する場合

今回の Material Design 化では、 スクローラブルな View と Toolbar が連動して動くやつは導入しませんでした。

が、もし、Activity が Toolbar を保持する構成に導入するとしたら、以下のようなレイアウト構成になります。

一応、 **Activity と Fragment をまたいで連動するアニメーションは可能なようです。**

```xml:Activity
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    android:id="@+id/coordinatorlayout"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbarlayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        >

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_scrollFlags="scroll|enterAlways"
            />

    </android.support.design.widget.AppBarLayout>

    <FrameLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        />

</android.support.design.widget.CoordinatorLayout>
```

```xml:Fragment
<android.support.v7.widget.RecyclerView
    android:id="@+id/recyclerview" 
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
```

![coodinatorlayout_with_recyclerview.gif](https://qiita-image-store.s3.amazonaws.com/0/35834/517807b1-06b8-2d4d-e8e2-2ab71e87d8fc.gif)

---

RecyclerView ではなく ListView を使うなら、 `android.support.v4.widget.NestedScrollView` で ListView を囲むようにレイアウトを組めばいけるようです。

> が、以下のようにレイアウトを組むと、ListView が画面いっぱいに表示されない現象が発生しました。原因は調べていないのでわかっていません...

```xml:Fragment
<android.support.v4.widget.NestedScrollView
    android:id="@+id/nestedscrollview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <ListView
        android:id="@+id/listview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        />

</android.support.v4.widget.NestedScrollView>
```

![coodinatorlayout_with_listview.gif](https://qiita-image-store.s3.amazonaws.com/0/35834/7463e9c3-043b-ff39-f39f-70792d7de530.gif)

## カスタムフォントに Noto フォントを使うとデザイン崩れが起きる

Material Design で使われているフォントは Roboto と Noto フォントです。フォントファイルは ガイドラインの Resources ページからダウンロードします。

[Resources– Roboto & Noto fonts](http://www.google.com/design/spec/resources/roboto-noto-fonts.html)

<img width="245" alt="noto_sans_cjk_jp.png" src="https://qiita-image-store.s3.amazonaws.com/0/35834/9cde03b8-01dc-014a-bf52-68c947c62a8e.png">

しかし、 Noto フォントをアプリに組み込んで使用すると、表示崩れが起きる現象が発生します。

現象と解決策は次の qiita 記事を参考にしました。詳しくはそちらの記事を見てください。（@konifar さんありがとうございました）

[AndroidでNotoフォント・Robotoフォントを使う](http://qiita.com/konifar/items/48f4f30710c9c89c5c0d)

## 既存アプリの Material Design 化は段階的にしよう

最も悩ましいのがこれだと思います。

新規アプリ開発であればいいのですが、既存アプリを Material Design 化するとなると、影響範囲はとんでもないです。工数も QA も大きいです。つらいです。つらいです。

今回携わった Material Design 化のお仕事は、既存アプリがそれほど規模が大きいものではなかったので「総書き換えしてこ↑↑↑」みたいな勢いでやりました。が、カネに直結している、規模が大きいアプリとなると、勢いだけではいけないです。

なので、Material Design 化は長期的・段階的に適応していくのが、リスクが少なくて済みそうだと思いました。例えば、 **まず ActionBar を Toolbar に置き換える** とか **padding や margin や [Keylines](http://www.google.com/design/spec/layout/metrics-keylines.html#metrics-keylines-keylines-spacing) を整える** から着手し始めて、後々に AppBarLayout や CoordinatorLayout、NavigationView の導入を順々にしていく、などです。

---

2014 年の Google I/O で Material Design が発表されてから幾日と経過しました。まだ iOS 風 UI のアプリや、独自 UI のアプリは、そろそろ Material Design に沿ってデザインを刷新してもいいと思います。Google, Android の世界観に、オレオレ UI という土足で踏み込むのではなく、Google, Android の世界観に馴染む足で踏み込んでいきたいところです。（Material Design の是非は置いといて...）