レイアウトリソースにボタンの処理を記述する

いままで Android で View のイベントを処理したい時は、 Java で OnClickListener 等を設定していました。
でもでも、実はレイアウトリソースに「実行したい処理」を記述できたんですね(*´ω`*)

どんな場合にこの方法で記述できるの?

この方法で記述できるのは、View の「クリックイベント」を処理する場合です。
すべてのイベントに対して使用することはできないんです。。

どんな風に便利なの?

一般に、 Javaイベントハンドラを記述するより、コードがシンプルになると思います。

また、ListView や GridView を使う時、各アイテムの中にボタンを設けたい場合が、まれに良くあります。この時、 Javaイベントハンドラを設定するコードは少し複雑になります。この場合もレイアウトリソースに記述したほうが、コードがシンプルになるように感じました。(記事の後半に例を載せています)

記述の仕方は簡単♪

  1. Activity に public メソッドを用意する
  2. レイアウトリソースにonClick属性を記述する

以下、詳しく見ていきます。

Activity に public メソッドを用意する

このメソッドがクリック時に呼び出されます。

class VoiceListActivity extends Activity {
public void play(View view) {
    // ...
}

引数は View になります。

レイアウトリソースにonClick属性を記述する

レイアウトリソースの中に、こんな風に記述します。

<ImageButton
  ...
  android:onClick="play" />

これだけでボタンを押したときに play() が呼ばれるようになります。シンプルですね(*´ω`*)

参考 http://developer.android.com/reference/android/view/View.html#attr_android:onClick

応用 - ListView の各アイテム内のボタン

ListView の各アイテムの中にボタンがあり、そのボタンを押したときの処理をこの方法で記述する場合の例です。

この場合、前述の内容に加え、以下の処理が必要になります。
(少し複雑です。。もっといい方法ないかすら。。)

  1. ListView の各アイテムに、そのアイテムの識別情報を設定しておく
  2. クリック処理を実行するメソッド内で、 どのアイテムのボタンがクリックされたのか調べる

以下、詳しく見ていきます。

ListView の各アイテムに、そのアイテムの識別情報を設定しておく

こんな感じです!

public class FooArrayAdapter<T> extends ArrayAdapter<T>{
  // ...

  @Override
  public View getView(int position, View convertView, ViewGroup parent) {
    // このメソッドは定型処理
    View view;
    if (convertView == null) {
      view = (View) inflater.inflate(itemLayout, parent, false);
    } else {
      view = convertView;
    }

    T item = getItem(position);
    updateView(view, item, position);
    return view;
  }

  @Override
  protected void updateView(final View view, final T item, int position) {
    // ★ここで設定! 
    view.setTag(R.id.model, item);
    // ...
  }

R.id.model は res/values/ids.xml 辺りに定義しておきます。

item は各アイテムの情報を保持する人です〜

クリック処理を実行するメソッド内で、 どのアイテムのボタンがクリックされたのか調べる
public void play(View view) {
  Object model = ViewUtils.getTag(view, R.id.model));
  play(model);
}

こんな感じのユーティリティを用意しておくと便利かもです(*´ω`*)

/**
 * view もしくはその先祖に紐づく tag を取得する
 */
public static Object getTag(View view, int id) {
  while (view != null) {
    Object tag = view.getTag(id);
    if (tag != null) {
      return tag;
    }

    if (view.getParent() instanceof View) {
      view = (View) view.getParent();
    } else {
      break;
    }
  }
  throw new IllegalArgumentException("View が tag と紐づいていません");
}

最後に♪

こんな方法があることを、最初から知りたかったです。。(´;ω;`)