注意! この記事はQiitaにて公開されていた内容をimportしたものです。
これらの内容は場合によっては陳腐化していて役に立たなくなっていたり、有害であったり、現在の著者の主張と異なることがあります。
皆様の判断の上でご利用いただけますと幸いです(度を超してヤバいものは著者に連絡して頂ければ対応します m(_ _)m)


追記(2014/10/23)

Android 5.0 SDKがリリースされたので、別観点からRippleエフェクトを解説しました。以下の記事も併せてどうぞ。

tl; dr

Githubにコードを公開しました!うまくいかない方はリポジトリをクローンして、そのまま実行してちょっとずつ試してみて下さい。

はじめに

Android L Developer Preview(以下、Android L)では、「Material Design」というUX体系が発表されました。

非常に多岐にわたり記載されてありますが、

  • Floating action button, a circular button made of paper that lifts and emits ink reactions on press.
  • Raised button, a typically rectangular button made of paper that lifts and emits ink reactions on press.
  • Flat button, a button made of ink that emits ink reactions on press but does not lift.

inkというワードが多く出てきますね。今回のデザインテーマで印象的な効果のひとつとして、インクが染み渡るようなボタンアクションが見受けられます。これは波紋(ripple)を描くことから、ripple effectだと書かれています。この効果を独自のカラーリングにしたり、形状を変えても使用できるようにすることで、アプリの表現の幅がぐっと広がるはずですので、是非とも自在にコントロールしたいところです。

まずはandroid:Theme.Material.Lightのデフォルトカラーを。一番上がdisabled状態、二番目がenabledな状態、三番目はenabledな状態かつまさに今タップされた状態です。

Screenshot_2014-06-29-04-01-10.png

ステップ1. アプリ全体でカラーをコントロールする

まず色を以下のように定義しておきます。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="button_color">#ffffaab3</color>
    <color name="highlight_color">#f00</color>
</resources>

次に、ボタンの有効・無効状態で色を変化させるために以下のカラーを定義します。 ボタンが無効状態の時はalphaを0.5にしてあるだけです。

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_enabled="false" android:alpha="0.5" android:color="@color/button_color"/>
    <item android:color="@color/button_color"/>
</selector>

また、layoutは以下のようにしました。 なんてことはない、styleの定義をしている部分以外は普通です。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:gravity="center"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/button01"
        style="@style/AppTheme.Button"
        android:text="Button Disabled"
        android:enabled="false"/>

    <Button
        android:id="@+id/button02"
        style="@style/AppTheme.Button"
        android:text="Button Enabled"/>

    <Button
        android:id="@+id/button03"
        style="@style/AppTheme.Button"
        android:text="Button Enabled"/>
</LinearLayout>

加えて、Activityクラスで各ボタンにクリックリスナーをセットするなりしてクリックできるようにしておきます。

以上のように定義した上で、以下の定義を記述します。(ぶっちゃけ直に書いても良い) android:colorControlHighlightは、ボタンのハイライトカラー以外にも、クリッカブルな所全てに適用されることに気をつけて下さい(例えばメニューボタン)。

<resources xmlns:tools="http://schemas.android.com/tools">
    <style name="AppTheme" parent="android:Theme.Material.Light">
        <!-- 実際はいくつかここに記述してありますが本稿と関係ないので省略 -->
        <item name="android:colorControlHighlight">@color/highlight_color</item>
        <item name="android:colorButtonNormal">@color/btn_material</item>
    </style>

    <style name="AppTheme.Button" parent="android:Widget.Material.Button">
        <!-- サイズと文字装飾の設定をしているだけなので本稿とは無関係 -->
        <item name="android:gravity">center</item>
        <item name="android:layout_width">match_parent</item>
        <item name="android:layout_height">48dp</item>
        <item name="android:textAllCaps">false</item>
    </style>
</resources>

最後に実行してみると、以下のように表示されるはずです。

Screenshot_2014-06-29-04-04-03.png

ステップ2. 9patchに定義する

ステップ1では、アプリ全体で主に使われるカラー設定ということになります。 ただ、一部分だけ色を変えたいというケースもあるかと思います。そういうときはどうするのが良いでしょうか。9patchとtintを使う方法を試します。

まずは、以下の色定義を追加します。

    <color name="button_blue_color">#ff9b9cff</color>
    <color name="button_blue_highlight_color">#00f</color>

次に、ボタンの有効・無効の色定義を作成します。

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_enabled="false" android:alpha="0.5" android:color="@color/button_blue_color"/>
    <item android:color="@color/button_blue_color"/>
</selector>

次に、9patch画像を作成します。基本色は#FFFで作成します。僕は以下のような画像をbtn_file.9.pngとして作りました。配置場所は適当にres/drawable-xhdpiとかで良いと思います。

btn_file.9.png

次が重要ですが、以下のdrawableファイルを作成します。

<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="@color/button_blue_highlight_color">
    <item>
        <nine-patch android:src="@drawable/btn_file"
            android:tint="@color/btn_blue_material" />
    </item>
</ripple>

<ripple>がAndroid Lで追加された新しい要素となります。

rippleのandroid:colorが波紋の色となります。rippleの子要素はitemを持ち、itemの中身はリソースを定義します。

nine-patch要素のandroid:tintは、9patchリソースの白色の部分を指定されたカラーでオーバーライドするような感じです。詳しくはyanzmさんの解説記事を。

実はbtn_blue_material.xmlファイルの内容はAndroid Lのリソースファイルとほぼ同じです。

最後に以下の記述をstyleファイルに追加します。

    <style name="AppTheme.Button.Blue">
        <item name="android:background">@drawable/btn_blue_material</item>
    </style>

実行結果は以下の通り。独自の形のボタンでも波紋を出すことに成功しています。

Screenshot_2014-06-29-04-44-49.png

ステップ3. shapeで定義する

簡単なボタンでわざわざ9patchを使うのは面倒ですし、解像度にあわせて画像を用意することにもなるので、簡単な形状のボタンはxmlで定義したくなります。どうすれば良いでしょうか。

まず、ボタンの形状を定義するshape resourceを作成します。 うまく汎用的に作る方法が分からなかったので色情報を直接埋め込んでいます。9patchであればtintを使い動的に色を定義できたのですが。。。

<?xml version="1.0" encoding="utf-8"?>

<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <solid android:color="@color/btn_blue_material" />
    <corners android:radius="20dp" />
    <size
        android:width="30dp"
        android:height="30dp" />
</shape>

次に、btn_blue_material.xmlを改変します。

<?xml version="1.0" encoding="utf-8"?>

<ripple xmlns:android="http://schemas.android.com/apk/res/android"
    android:color="@color/button_blue_highlight_color">
    <item>
        <inset android:drawable="@drawable/btn_rounded"
            android:insetTop="4dp"
            android:insetBottom="4dp"
            android:insetLeft="4dp"
            android:insetRight="4dp"/>
    </item>
</ripple>

以下のように表示されることでしょう。

Screenshot_2014-06-29-04-47-26.png

まとめ

Material DesignでのRipple effectはユーザにとって印象に残りやすく、それ故にこれを使っていないボタンは違和感を与えることになると思います。是非とも使いこなしたいですね!