第5回は、実際にアプリを作りながら、Material Designをどのように用いるのか説明していきます。アプリ開発を手順ごとに試せる「コードラボ」を活用して、実際のアプリを開発しましょう。

Material Component

Googleでは、Material ComponentというMaterial Design対応アプリの開発をサポートするライブラリをAndroid、iOS、Web用に提供しています。このMaterial Componentを使うことで、アプリを迅速に作成できます。

Step 1 : 開発環境を用意する

公式IDEのAndroid Studioを利用して、Androidのアプリを開発してみましょう。

Android Studioは開発ツールで、ディスプレイの解像度やCPUが異なるAndoird端末にアプリを最適化できます。コード編集やデバッグ、パフォーマンス解析、柔軟性の高いビルドシステム、素早いデプロイ・ビルドが可能になります。Android Studioは、こちらのサイトからダウンロードしてください。

Step 2 : アプリのベースを作成する

Android Studio では、テンプレートを利用したアプリ製作が可能です。既に、Material Designの要素を含むテンプレートも用意されていますが、今回は何も含まれていない「Empty Activity」というテンプレートを選択します。

テンプレートを選択したら、Android Studio の画面の右上にある、Run (三角形のボタン)を押します。

Android端末をUSBで接続した場合は、ここでアプリがインストールされ、Android上に作成したアプリが表示されます。

このように、”Hello World” が表示されれば、第一段階は完了です。

今回は、最低限のデザインが実装されたアプリを使っていきますので、こちらのサイトからStarterアプリ(zipファイル形式)をダウンロードします(GitHubでクローンする方法もあります)。

zipファイルをダウンロードしたら解凍し、以下のように実行してください。

  1. Android Studioを起動して、Import Projectを選択
  2. Starterディレクトリを選択
  3. OKをクリック

ここまで来たら、先ほどのEmpty Activityの時と同じように、Android端末をUSBケーブルで繋いだ状態で、以下のRun (三角形のボタン) を押してください。

すると以下のような、サンプルアプリがAndroid端末で起動します。

Step 3 : Material Component をプロジェクトに追加する

Starterアプリが起動したら、これをベースにアプリを作っていきます。

まずは、Material Componentsを追加し、 Material Designの各コンポーネントを使えるように準備します。ビルドを自動化するファイルであるGradleファイルを初めに開きます。ライブラリをプロジェクトに追加する場合は、 Gradleファイルに記述していきます。

app/build.gradle

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])

    compile 'com.android.support:appcompat-v7:25.3.1'
    compile 'com.android.support:cardview-v7:25.3.1'
    compile 'com.android.support:recyclerview-v7:25.3.1'
    compile 'com.android.volley:volley:1.0.0'
    compile 'com.google.code.gson:gson:2.2.4'

}

Gradleファイルには、こうした記述が既にあるため、このファイルに、

compile 'com.android.support:design:25.3.1' 

というコードを記述するとアプリ全体でMaterial Design Componentが使えるようになります。この際、常に最新版のファイルを利用するようを心がけてください。

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])

    compile 'com.android.support:appcompat-v7:25.3.1'
    compile 'com.android.support:cardview-v7:25.3.1'
    compile 'com.android.support:recyclerview-v7:25.3.1'
    compile 'com.android.volley:volley:1.0.0'
    compile 'com.google.code.gson:gson:2.2.4'

    // この行を追加:
    compile 'com.android.support:design:25.3.1'
}

となっていることを確認してください。

Step 4 : Collapsing Toolbarで、折りたたみできるツールバーを作る

Material Designで重要なポイントは、「大きなイメージ画像を効果的にアプリで表現する」ということです。効果的に大きなイメージ画像を表示したい場合、最適なコンポーネントは「Collapsing Toolbar」になります。

Collapsing Toolbarでは、さまざまな異なるViewを管理するLayoutの「Coordinator Layout」をMain Activityに追加します。shr_main.xmlファイルを開いて、FrameLayoutのタグの部分を以下のようなCoordinatorLayoutに置き換えてください。

app/res/layout/shr_main.xml

<android.support.design.widget.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.CoordinatorLayout>

CoordinatorLayoutを追加したら、ToolbarにAppBarLayoutとCollapsingToolbarLayoutを追加します。追加する箇所としては、Coordinator Layoutのタグの直下になります。

app/res/layout/shr_main.xml

<!-- ... -->
<android.support.design.widget.AppBarLayout
    android:layout_width="match_parent"
    android:layout_height="400dp">

    <android.support.design.widget.CollapsingToolbarLayout
        android:id="@+id/collapsing_toolbar"
        style="@style/Widget.Shrine.CollapsingToolbar"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">


        <!-- Wrap this view: -->
        <android.support.v7.widget.Toolbar
            android:id="@+id/app_bar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"/>

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

</android.support.design.widget.AppBarLayout>
<!-- ... →

上記のコードを記述すると、赤文字のエラーが出ます。

エラー

エラーの理由は、Collapsing Toolbarで使用するStyleを読み込めていないから。Styleとは、「Collapsing Toolbarを何色にするのか?」や「マージンを幾つにするのか?」といった装飾部分を指定することです。Styleを指定するためにはstyles.xmlを開き、コメントアウトされている部分を外しましょう。

では、Collapsing Toolbarを追加したところで実際のアプリを見てみましょう。

「Shrine」と書いてある文字がスクロールに合わせて縮小し、上のToolbarに格納されることを確認できます。

ここから、Collapsing Toolbar内にコンテンツを追加していきます。コンテンツを追加するためには、LayoutとActivityのファイルの両方にコードを記述する必要があります。

Layoutに以下を記述します。

app/res/layout/shr_main.xml

<!-- ... -->
<android.support.design.widget.CollapsingToolbarLayout
    android:id="@+id/collapsing_toolbar"
    style="@style/Widget.Shrine.CollapsingToolbar"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">


    <!-- Add this view -->
    <com.android.volley.toolbox.NetworkImageView
        android:id="@+id/app_bar_image"
        style="@style/Widget.Shrine.CollapsingToolbarImage"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        app:layout_collapseMode="parallax"
        app:layout_collapseParallaxMultiplier="0.75"/>

    <android.support.v7.widget.Toolbar
        android:id="@+id/app_bar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        app:layout_collapseMode="pin"/>

</android.support.design.widget.CollapsingToolbarLayout>
<!-- ... -->

次に画像をロードする処理をMainActivity.ktに記載します。

app/java/MainActivity.kt の50行目に

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.shr_main)

     setSupportActionBar(app_bar)

    val products = readProductsList()
    val imageRequester = ImageRequester.getInstance(this)

    // Add this code:
    val headerProduct = getHeaderProduct(products)
    imageRequester.setImageFromUrl(app_bar_image, headerProduct.url)

    product_list.setHasFixedSize(true)
    product_list.layoutManager = LinearLayoutManager(this)
    adapter = ProductAdapter(products, imageRequester)
    product_list.adapter = adapter
}

// Add this code as well:
private fun getHeaderProduct(products: List<ProductEntry>): ProductEntry {
    if (products.isEmpty()) {
        throw IllegalArgumentException("There must be at least one product")
    }

    for (i in products.indices) {
        if ("Perfect Goldfish Bowl" == products[i].title) {
            return products[i]
        }
    }
    return products[0]
}

を追加してください。

ここまで終わったら、Runをクリックします。

すると、このようにCollapsing Toolbarの背景に画像が表示されるようになりました。

Step 5 : Bottom Navigation を追加する

Collapsing Toolbarが完成したら、画面間の移動をする上で使う「Bottom Navigation」を追加しましょう。

Bottom NavigationもMaterial Componentのライブラリとして提供されているため、新たにライブラリを追加する必要がありません。Bottom Navigationを利用するには、Layoutファイルに以下のコードを記述します。

app/res/layout/shr_main.xml

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

    <!-- Add this view -->
    <android.support.design.widget.BottomNavigationView
        android:id="@+id/bottom_navigation"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:background="#ffffff"
        android:theme="@style/ThemeOverlay.Shrine.BottomNavigation"
        app:menu="@menu/shr_bottom_navigation"/>

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

Bottom Navigationは、他のコンテンツより手前に位置する要素なので、shr_main.xmlの最後に上記のコードを記述します。

コードを追加すると、以下のように、Android Studioでエラー部分が赤く表示されます。

エラー

これは、Collapsing Toolbarと同じように、Styleの変数が見つからないエラーです。先ほど同じく、styles.xmlにBottom Navigation のスタイルを追加しましょう。

app/res/values/styles.xml

<!-- ... -->
    <style name="ThemeOverlay.Shrine.BottomNavigation" parent="">
        <item name="colorPrimary">?attr/colorAccent</item>
    </style>

</resources>

ただし、Styleを追加しても今度はBottom Navigationに表示するメニューが無いと指摘されます。Bottom Navigationは、画面間を移動するコンポーネントなので、メニューを追加する必要があるからです。

メニューを作成するには、resのディレクトリ内にmenuというディレクトリを作成して、その中に、shr_bottom_navigation.xmlというメニューを表示するxmlを作成します。そして、shr_bottom_navigation.xmlに以下のコードを記述することで、Navigation内にメニューが表示されます。

app/res/menu/shr_bottom_navigation.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <item
        android:id="@+id/category_clothing"
        android:enabled="true"
        android:title="@string/shr_category_clothing"
        android:icon="@drawable/ic_favorite_vd_theme_24"
        app:showAsAction="ifRoom"/>
    <item
        android:id="@+id/category_home"
        android:enabled="true"
        android:title="@string/shr_category_home"
        android:icon="@drawable/ic_shrine_vd_theme_24"
        app:showAsAction="ifRoom"/>
    <item
        android:id="@+id/category_popsicles"
        android:enabled="true"
        android:title="@string/shr_category_popsicles"
        android:icon="@drawable/ic_shopping_cart_vd_theme_24"
        app:showAsAction="ifRoom"/>
</menu>

これで、Runを押すと、次のように表示されます。

ただし、実際にBottom Navigationのメニューをタッチしても、ページが遷移しません。遷移させるためには、MainActivity.ktのファイルにコードを加える必要があります。

今回のケースでは、複数のページを用意せず、Bottom Navigationが押されたら、コンテンツがシャッフルする動作を指定します。その場合、Activityファイルに処理を記述する必要があります。

具体的には、app/java/MainActivity.ktファイルに以下を記述します。

app/java/MainActivity.kt

private fun shuffleProducts() {
    val products = readProductsList()
    Collections.shuffle(products)
    adapter?.setProducts(products)
}

そして、onCreateメソッド内に以下の処理を書きます。

bottom_navigation.setOnNavigationItemSelectedListener {
        val layoutManager = product_list.layoutManager as LinearLayoutManager
        layoutManager.scrollToPositionWithOffset(0, 0)
        shuffleProducts()
        true
    }

これで、Bottom Navigationをクリックするごとに、コンテンツがシャッフルされます。

Step 6 : レイアウトを調整する

コンテンツを縦に並べる場合、一画面あたりに表示されるコンテンツ数に限界がありますので、レイアウトを調整しましょう。

一つの画面でより多くのコンテンツを見せるためには、2列表示が良いでしょう。そのためには、新しくintegers.xmlというファイルをvalueフォルダ内に作成して、以下のようなコードを記述します。

app/res/values/integers.xml

<resources>
    <integer name="shr_column_count">2</integer>
</resources>

この記述によって、2列表示が可能になります。ただ、この記述ではタブレットなどの大きなスクリーンで表示した場合でも、2列で表示されてしまいます。そうした状況を回避する場合は、横幅が480dpよりも大きい場合は3列、900dpよりも大きい場合は6列といったように、その分だけ以下のファイルを追加してください。

app/res/values-w480dp/integers.xml

<resources>
    <integer name="shr_column_count">3</integer>
</resources>

app/res/values-w960dp/integers.xml

<resources>
    <integer name="shr_column_count">6</integer>
</resources>

このようにファイルを追加すれば、ポートレートモードでは2列表示、ランドスケープモードでは3列といったように、コンテンツを出し分けることが可能になります。

実際に表示する場合は、GridLayout Manager を利用する必要があるため、以下のコードをMainActivity.kt に追加してください。

app/java/MainActivity.kt

override fun onCreate(savedInstanceState: Bundle?) {
/* ... */

    product_list.setHasFixedSize(true)
    product_list.layoutManager = GridLayoutManager(this, resources.getInteger(R.integer.shr_column_count))
    adapter = ProductAdapter(products, imageRequester)
    product_list.adapter = adapter

/* ... */
}

サンプルアプリを使うことで、Material Design Component を利用して、Android アプリを作る練習ができました。ぜひお試しください。

著者紹介


鈴木 拓生(すずき たくお)
Developer Relations Team Program Manager @ Google

メディア系企業にて、社外のデベロッパーと恊働するプロジェクト等を担当し、 2012 年からはシリコンバレーのスタートアップやインキュベーションに対しての投資も担当。

現在は Google の Developer Relationsチームで主にDesign SprintやMaterial Design周りのコミュニケーションを担当し、デベロッパーやスタートアップのエコシステムの向上を支援