Android で画像なしの光沢グラデーション
スマートフォンなどでよく見かける光沢のあるグラデーションを持ったツールバーを画像なしで作成してみる。Android ではコントロールの描画方法を drawable リソースから指定可能。これは画像と XML に大別される。
画像はデザインの自由度が高く美しいのだが解像度 (ldpi、mdpi、hdpi、...etc) と引き延ばしを意識する必要がある。詳しくは Draw 9-patch を参照のこと。有志による日本語訳はこちら。一方 XML はデザイン指定の方法がかなり限定されるため希望どおりの描画結果を得るのが難しい。
dp
または dip
のような密度非依存ピクセルによるサイズ指定と標準で用意されている形状・塗りは解像度に依存しないため、様々な端末への対応が容易になる。dp
や dip
については Supporting Multiple Screens を参照のこと。有志による日本語訳はこちら。
これらの立ち位置は Web 開発における CSS と画像の関係によく似ている。
Web 開発では描画に関する指定をなるべく CSS、それが難しい箇所で画像を用いる。Android でも同様にアイコンなど複雑な形状を持つものは画像、それ以外は XML というように使い分けることでメンテナンス性が向上する。
さて本題に入ろう。まずは光沢グラデーションがどのように構成されているかを画像で示す。
下地となるグラデーションに半分の高さをもつ半透明な陰影を乗せることで、あたかも光沢があるように見える。これを Android の drawable リソースで再現すると以下のようになる。
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- グラデーション下地 -->
<item>
<shape android:shape="rectangle">
<gradient
android:angle="270"
android:startColor="#DDDDDD"
android:endColor="#000000"
android:type="linear" />
</shape>
</item>
<!-- グラデーション陰影 -->
<item android:top="20dp">
<shape android:shape="rectangle">
<solid android:color="#40000000" />
</shape>
</item>
</layer-list>
layer-list
を利用すると複数の形状をレイヤーとして重ね合わせた状態を定義できる。各レイヤーは item として表し、その中へ shapr
などを入れ子にできる。
この定義は前述の画像のように下地を先に描いてその上に陰影を重ねている。陰影レイヤーとなる item
の android:top
が垂直方向の描画を開始する始点となる。これは描画領域における相対位置となり他にも bottom
、left
、right
を指定可能。相対位置ではあるが感覚としては余白の指定に近い。
位置の指定はディメンション値 (dp
、px
、...etc) となっているので 50%
といった割合の定義はできないので注意する。そのような指定を行いたい場合は対象となる領域のサイズを決めてからその割合を固定値として設定する。
今回は領域の高さを 40dp
としてその下半分へ陰影が付くように android:top="20dp"
とした。割合で指定できないのは仕様としてはイマイチ。しかし Android アプリの画面と GUI の点数はそれほど多くないから特に困ることもないだろう。
これをツールバーの背景に利用するとして次はその上に乗るボタンを定義してみたい。デザイン的には周辺にくぼみを持つボタンとする。
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<!-- くぼみ -->
<item>
<shape android:shape="rectangle">
<corners
android:bottomRightRadius="4dp"
android:bottomLeftRadius="4dp"
android:topLeftRadius="4dp"
android:topRightRadius="4dp" />
<gradient
android:angle="270"
android:startColor="#CC222222"
android:endColor="#CCEEEEEE"
android:type="linear" />
</shape>
</item>
<!-- グラデーション下地 -->
<item
android:top="2dp"
android:left="2dp"
android:right="2dp"
android:bottom="2dp">
<shape android:shape="rectangle">
<corners
android:bottomRightRadius="4dp"
android:bottomLeftRadius="4dp"
android:topLeftRadius="4dp"
android:topRightRadius="4dp" />
<gradient
android:angle="270"
android:startColor="#DDDDDD"
android:endColor="#222222"
android:type="linear" />
</shape>
</item>
<!-- グラデーション陰影 -->
<item
android:top="16dp"
android:left="2dp"
android:right="2dp"
android:bottom="2dp">
<shape android:shape="rectangle">
<corners
android:bottomRightRadius="0dp"
android:bottomLeftRadius="0dp"
android:topLeftRadius="4dp"
android:topRightRadius="4dp" />
<solid android:color="#40000000" />
</shape>
</item>
</layer-list>
少し長いけど考え方はツールバーと同じ。
一つ目のレイヤーでくぼみを定義する。これは上から下へ暗くなるグラデーション。corners
指定はすべての角を丸めるようにする。透過と明るさを変更することで、くぼみ具合を調整できる。
二つ目のレイヤーはボタン部分の下地。これは前述のツールバー下地と同じような定義となる。描画の開始をすべて 2dp
で指定することにより描画領域に対して 2dp
の余白がでる。この部分へくぼみが描画される。つまりくぼみが枠線的に扱われるため corners
も同様の設定にしておく。
最後のレイヤーでボタンの下地に対する陰影を定義する。透過ありの単色塗りである。これはボタンの下半分に乗せるため corners
は下側だけ丸めるようにする。上も丸めてしまうとボタンの中腹にそれが表示されて不自然になるので注意する。
以上でツールバーとボタン用の光沢グラデーション指定ができた。さっそく利用してみよう。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#FF6A00"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<LinearLayout
android:background="@drawable/toolbar"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="40dp">
<Button
android:background="@drawable/toolbar_button"
android:text="TEXT"
android:textColor="#FFFFFF"
android:textStyle="bold"
android:textSize="16dp"
android:shadowColor="#000000"
android:shadowRadius="2.0"
android:shadowDx="1.0"
android:shadowDy="2.0"
android:paddingLeft="8dp"
android:paddingRight="8dp"
android:paddingTop="4dp"
android:paddingBottom="4dp"
android:layout_marginLeft="4dp"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="32dp" />
</LinearLayout>
</LinearLayout>
これを Android エミュレータで表示すると以下のようになる。
色や透明度を少し変更するだけでかなり見た目が変わる。実に面白い。最後に今回作成したサンプルのプロジェクトを公開しておく
Android 2.1 update 1 (API Level 7) でビルドし、エミュレータと初代 Xperia (SO-01B) にて動作確認した。