2012年4月6日金曜日

AndroidのボタンをShapeDrawableで描画するには?

ベクター描画のオルタナティブ「Shape Drawable」

SVGでボタンを描画するのは、パフォーマンス的に問題があることがわかった。

琴線探査: AndroidのボタンをSVGで描画するには?

琴線探査: 続・AndroidのボタンをSVGで描画するには?(ローテクだけど高速版)

AndroidにはSVGと似たような方法でベクターのイメージを描画する方法がある。「Shape Drawable」だ。res/drawableフォルダにShapeを定義したXMLを配置すると参照、描画できる。

Drawable Resources | Android Developers

この方法がどれだけ速いか試してみよう。


ボタンとなるXMLを作成

まず、ボタンとなるShape DrawableのXMLを作成する。

res/drawableフォルダを作成する。その下に、次の2つのXMLを配置する。

roundbutton_up.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >

    <solid android:color="#444444" />

    <padding
        android:bottom="8dp"
        android:left="8dp"
        android:right="8dp"
        android:top="8dp" />

    <corners android:radius="4dp" />

    <stroke
        android:width="2dp"
        android:color="#FFFFFF" />

</shape>


roundbutton_down.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle" >

    <solid android:color="#999999" />

    <padding
        android:bottom="8dp"
        android:left="8dp"
        android:right="8dp"
        android:top="8dp" />

    <corners android:radius="4dp" />

    <stroke
        android:width="2dp"
        android:color="#FFFFFF" />

</shape>


ShapeDrawableResourceButtonクラス作成

ex.sdrbパッケージにShapeDrawableResourceButtonクラスを作成する。

ShapeDrawableResourceButton.java

package ex.sdrb;

import java.util.Date;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.TextView;


public class ShapeDrawableResourceButton extends TextView {


  protected Drawable dUp;
  protected Drawable dDown;


  public ShapeDrawableResourceButton(Context context, AttributeSet attrs) {
    super(context, attrs);
    setClickable(true); // これをしないとタッチイベントがDOWNのみになる
    init();
  }


  protected void init() {

    Log.v("init", "スタイリング開始");
    long start = new Date().getTime();

    // リソース取得
    Resources res = getResources();

    // UP用Drawable取得
    dUp = (Drawable) res.getDrawable(R.drawable.roundbutton_up);

    // DONW用Drawable取得
    dDown = (Drawable) res.getDrawable(R.drawable.roundbutton_down);

    // UP用Drawableを背景にセット
    setBackgroundDrawable(dUp);

    long end = new Date().getTime();
    Log.v("init", "スタイリング終了 " + (end - start) + " msec");

  }


  @Override
  public boolean onTouchEvent(MotionEvent event) {

    // タッチイベントによって表示するDrawableを変える
    String action = "";
    switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN:
        action = "ACTION_DOWN";
        setBackgroundDrawable(dDown);
        break;
      case MotionEvent.ACTION_UP:
        action = "ACTION_UP";
        setBackgroundDrawable(dUp);
        break;
    }
    Log.v("onTouchEvent", "action = " + action);

    // 再描画リクエスト
    invalidate();

    return super.onTouchEvent(event);

  }


}

SVGButtonのプログラムと比べると大幅に単純になっている。

XML読み込みの部分をResourcesクラスがやってくれるのと、画面密度によるスケーリングを自分で処理しなくて良いということが大きい。


レイアウトを編集

res/layout/main.xmlをこのように編集する。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical"
    android:padding="10dp" >

    <ex.sdrb.ShapeDrawableResourceButton
        android:id="@+id/btn1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_margin="5dp"
        android:text="ShapeDrawableResourceButton"
        android:textColor="#FFFFFF" />

</LinearLayout>


実行結果


ボタンをクリックするとハイライトする。

同様の処理をSVGButtonで行った時は4000msec程度、SVGButtonNoDOMで行った時は1100msec程度、そして今回は何と100msec程度!SVGButtonNoDOMと比べても11倍も速い。圧倒的だ。

Eclipseプロジェクトファイル:ShapeDrawableResourceButton.zip (157KB)


問題点

まず問題なのは、このShapeDrawableを使う方法では複雑なグラフィックを表現できないことだ。基本的に四角、楕円、線、リングしか描けない(^^);

もう1つの問題は、Resourceから取得したDrawableの色などをプログラムで変更できないこと。DrawableのAPIドキュメントを見る限り、残念ながらできなさそう。

ただ、このスピードは捨てがたいなぁ。