2011-11 << 2011-12 >> 2012-01

2011-12-13 (火)

*[Android] Drawable Resource XML でお絵かきする

Androidでボタンなどのコンポーネントの見た目をカスタマイズしたい場合,background等の属性にdrawable以下にある画像ファイルやxmlを指定すると思います.ここで画像を使うと画面の解像度ごとに用意しないと綺麗に表示できないし,サイズが変わるボタンなどは9-Patch等で引き伸ばし方を調整しないといけないので面倒です.

なので簡単なボタンくらいならxmlだけで作った方が楽です.画像を使わずに作っておくといくつか利点があります.

  • 画面の解像度に依存しない
  • 画像よりファイルサイズが小さい
  • テキストエディタで編集できる

ただ,あまりdrawableの使い方詳しく説明されてる情報が見つからないし,リファレンス見ても???な部分があるので,実際に書いて試行錯誤したりよく分からないところはAndroidのソース読んで確認する必要がありました.

説明書くつもりだったけど,面倒になったので細かい使い方はAndroidのリファレンスとコードを参照してください.

基本図形(shape)

四角形,楕円,線分,リング("rectangle" | "oval" | "line" | "ring")の4種類だけです.四角形は角を丸くできます.ringはlevelの値を使って弧にすることもできる気がします.

colorとかsizeとかを要素として持てます.sizeを指定しても,drawableのサイズに合わせて引き伸ばされてしまうので,item等に入れて,top,bottom,left,rightを指定する必要があるみたいです(?).

layer-list

複数のdrawableを重ねて1つのdrawableにすることができます.

layer-listの子は複数のitemで,このitem1つにshapeなどのdrawableを入れられます.

rotate

なぜかリファレンスには載ってませんが角度を指定して回転できます.アニメーション時に使うものですが,静的なdrawable内にあっても大丈夫なようです.

ただ動作が怪しくて,回転の中心が上手く指定できずに,結果を見ながら試行錯誤しないと使えません.けっこう不便です.

……と思っていたら,Android4.0や最新のADTだと普通になった.もしかしてバグだったのか.ただ,2.3とかの実機だと今までどおり変な動作する.

アニメーション以外では使わないほうが良さそうです.

ボタン

player1213.png

こんな感じのボタンが欲しかった.

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
    <item android:state_pressed="true">
        <shape>
          <corners android:radius="4dip" />
          <padding android:left="8dp" android:right="8dp" android:top="2dp"  android:bottom="2dp"/>
          <solid android:color="#7799ff" />
        </shape>
    </item>
    <item android:state_selected="true">
        <shape>
          <corners android:radius="4dip" />
          <padding android:left="8dp" android:right="8dp" android:top="2dp"  android:bottom="2dp"/>
          <solid android:color="#7799aa" />
        </shape>
    </item>
    <item android:state_focused="true">
        <shape>
          <corners android:radius="4dip" />
          <padding android:left="8dp" android:right="8dp" android:top="2dp"  android:bottom="2dp"/>
          <solid android:color="#7799aa" />
        </shape>
        <color android:color="#00ffffff" />
    </item>
    <item>
        <shape>
          <corners android:radius="4dip" />
          <padding android:left="8dp" android:right="8dp" android:top="2dp"  android:bottom="2dp"/>
          <gradient
              android:centerY="0.4"
              android:startColor="#88ffffff"
              android:centerColor="#66aaaabb"
              android:endColor="#44888899"
              android:angle="270" />
          <stroke android:width="1px" android:color="#aa888888"/>
        </shape>
    </item>
</selector>

グラデーションかけて縁取りするだけで済ませました.光沢とか影を表現したければ,layer-listで重ねれば良い感じにできると思います.

シークバー

SeekBarの見た目をカスタマイズするためには,progressDrawableとthumbを指定します.progressDrawableがプログレスバー部分で,thumbはつまみです.

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
  <item android:id="@android:id/background">
    <layer-list>
     <item>
      <shape>
        <corners android:radius="1dip" />
        <gradient
          android:startColor="#80666666"
          android:centerColor="#86999999"
          android:centerY="0.75"
          android:endColor="#a0666666"
          android:angle="270" />
      </shape>
     </item>
     <item>
      <shape android:shape="line">
        <stroke android:width="4dp" android:color="#dd444444" />
      </shape>
     </item>
    </layer-list>
  </item>
  <item android:id="@android:id/secondaryProgress">
    <clip>
      <shape android:shape="line">
        <stroke android:width="4dp" android:color="#ffffffff" />
      </shape>
    </clip>
  </item>
  <item android:id="@android:id/progress">
    <clip>
      <shape android:shape="line">
        <stroke android:width="4dp" android:color="#ffffbb33" />
      </shape>
    </clip>
  </item>
</layer-list>

background,progress,secondaryProgressをidで指定するだけです.どうやってバーの長さを変えてるのかと思っていたのですが,clipを使って部分的に表示していたようです.

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
  <item>
    <shape android:shape="rectangle">
      <size android:width="20dp" android:height="32dp" />
      <corners android:radius="2dip" />
      <padding android:left="8dp" android:right="8dp" android:top="2dp"  android:bottom="2dp"/>
      <gradient
          android:centerY="0.4"
          android:startColor="#88ffffff"
          android:centerColor="#66aaaabb"
          android:endColor="#44888899"
          android:angle="270" />
      <stroke android:width="1px" android:color="#aa888888"/>                         
    </shape>
  </item>
</selector>

つまみの方はボタンと同じように複数のステートがありますが,今回は常に同じ表示にしてしまいました.気をつけないといけないのはsizeを明示的に指定しないと大きさが0になってしまうためか,何も表示されません.

ドロイド君

Androidなのでドロイド君を描いてみます.

厄介なのがドロイド君の頭です.半円形の図形が欲しい.

普通にovalをclipすれば良いと思ってたのですが,表示する割合を決めるlevelという値は,xmlから指定できませんでした.

まぁ,ここまでは想定内です.

Shapeを継承しているArcShapeがあるのでこれを使うのが正攻法かな.しかし,リファレンスでshapeに指定できるものを見ても

"rectangle" | "oval" | "line" | "ring"

の4種類しかありません.

/graphics/java/android/graphics/drawable/Drawable.java

ソース見てがっかり.上手く書けば,任意のDrawableオブジェクトを作れるのかと思ったら,生成されるオブジェクトはif ~ else if ~でハードコーディングされていた.

そもそもshapeはShapeDrawableではなくGradientDrawableを生成している…….リファレンスで言ってること違います.

とりあえず諦めて,グラデーションでごまかす.

android1213.png android_icon.xml

まとめ

ここまで書いておいてなんですが,あまり凝ったことはやらない方が吉です.

svgのサブセットとかにして欲しかった.もしくは,cssでデザインできればもっと良い気がする.

2011-11 << 2011-12 >> 2012-01