現在位置: HyperLand Plusトップページ > 日曜日のAndrodアプリ開発 > クリックイベントを処理する

クリックイベントを処理する

Androidに限らず、ユーザ操作などのイベントをプログラム側で処理する仕組みをイベントリスナーと呼びます。イベントリスナーで1番初めに想像されるのは、クリックイベントではないでしょうか?このページでは、ボタンをクリックした場合のイベントリスナーの実装方法を取りまとめたページとなります。

クリックに関する、代表的なものに「OnClickListener」インターフェースを実装するか、「OnClickListener」インターフェースの匿名クラスを定義するか、レイアウトに「onClick」を定義するかの3種類がありますので順に説明します。本ページで出てくるキーワードを以下に取りまとめています。

イベント
個人的には、Android OSからアプリに通知されるものはずべてイベントと思っていますが、本サイトでは、さらに狭義のボタンをタップしたやアイテムが選択されたなどのユーザー操作をイベントとしています。
イベントソース
イベントソースは、イベントの発生源となるオブジェクトを指します。Androidであれば、ボタンやリストビューなどのUIが思い浮かぶと思いますが、レイアウト(レイアウトリスナー)などもイベントの発生源となるため、イベントソースとなります。イベントを処理するためには、イベントソースにイベントリスナーを登録します。
イベントリスナー
Android OSが通知したイベントをイベントソースが受け取り、イベントを処理する仕組みを指します。
イベントドリブン(イベント駆動型)
イベントを受け取り処理する仕組み

01.プロジェクトの作成

「クリックイベントを処理する」では、以下で要領でプロジェクトの作成を行っています。メニューは使わないのですが、1から設定するのは面倒なため、「Blank Activity」を選択しています。TitleとMenu Resorce Nameは使用しないため、デフォルトのままで大丈夫です。

プロジェクト作成
Application nameButtonClick
Company Domaincom.example.android.buttonclick
Minimum SDKAPI 10: Android 2.3.3 (Gingerbread)
Add and activity to MobileBlank Activity
Actiity NameButtonClick
Layout Nameactivity_button_click
Title任意
Menu Resorce Name任意

02.レイアウトの作成

「クリックイベントを処理する」では、以下のレイアウトを使用します。レイアウトですがボタン1つだけ配置し、ボタンのIDはデフォルトの「@+id/button」としています。「android:text="トースト表示"」の「トースト表示」部分は、本来「Strings.xml」に書くべきですが、ホームページスペースの都合上ベタ書きしています。

<RelativeLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:context=".ButtonClick">

  <Button
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:text="トースト表示"
      android:id="@+id/button" />

</RelativeLayout>

作成したレイアウトは下図左のようになります。下図左は、本ページで紹介してる結果で、ボタンをタップするとトーストが表示されます。

図1)ボタンクリック
ボタンクリック

03-01.View.OnClickListenerを実装する

まず、「View」クラスの「OnClickListener」インターフェースを実装し、OnClickListenerインターフェースの抽象メソッド「onClick」をオーバーライドします。ソースは以下のようになります。1つ目の解説となりますので若干厚めに説明します。

public class ButtonClick extends Activity implements View.OnClickListener {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_button_click);
    Button button = (Button) findViewById(R.id.button);
    button.setOnClickListener(this);
  }

  @Override
  public void onClick(View v) {
    if (v.getId() == R.id.button){
      Toast.makeText(this, "ボタンがクリックされました", Toast.LENGTH_LONG).show();
    }
  }
}

インターフェースを実装する場合、インターフェースに定義されているメソッドを、すべてオーバーライドする必要があります。View.OnClickListenerインターフェースの場合、onClickメソッドが該当しますので、onClickをオーバーライドします。onClickメソッドをオーバーライドしなければコンパイルエラーとなります。※アノテーションの「@Override」は、「スーパークラス(親クラス)のメソッドをオーバーライドして使用しています」と言う意味ですので消さないでください。

onClickメソッドは、クリック時に呼び出されメソッド内に記述した処理が実行されます。このようにイベント発生時に呼び出されるメソッドを「コールバックメソッド」と呼びます。

public class ButtonClick implements View.OnClickListener {
  @Override
  public void onClick(View v) {
  }
}

上記で指定したプロジェクトの初期状態では、「ActionBarActivity」クラスを継承していると思いますが、互換性を考慮し「Activity」クラスに変更します。「onCreateOptionsMenu」メソッドと「onOptionsItemSelected」メソッドを削除し「onCreate」メソッドのみを残します。

onCreateメソッド内で、「Button」クラスの変数宣言をし、「findViewById(R.id.button);」を使ってレイアウトから定義したボタンウィジェット「@+id/button」のリソースをButton変数に代入します。「findViewById」は、結果をViewクラスで返すため、ButtonクラスでキャストしButton変数に代入してあげます。

「button.setOnClickListener(this);」で、ボタンクリックのイベントが取得できるようにイベントリスナーを設定します。なお、thisはActivityを継承した自クラスを指します。

public class ButtonClick extends Activity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_button_click);
    Button button = (Button) findViewById(R.id.button);
    button.setOnClickListener(this);
  }
}

最後に、オーバーライドしたonClickメソッドにボタンがクリックされた際の処理を書きます。onClickメソッドの引数Viewには、クリックイベントの情報が含まれていますので、getIdメソッドを使用しViewからリソースIDを取得します。レイアウトで設定したボタンのリソースID「@+id/button」と比較し一致した場合にトーストを表示させています。比較対象が1つしかないため、IF文で比較してますが、複数比較する場合は、Switchを使用してください。

@Override
public void onClick(View v) {
  if (v.getId() == R.id.button){
    Toast.makeText(this, "ボタンがクリックされました", Toast.LENGTH_LONG).show();
  }
}

03-02-01.外部クラスファイルで定義する

以下のように、View.OnClickListenerインターフェースを実装したクラスファイルを作成し、ButtonClickクラス側で、「button.setOnClickListener(new ButtonClickListener());」と定義するとボタンクリックに関するイベントを処理することが可能ですが、このままではトーストを表示するなどのレイアウトに関する処理が何も記述できません。

class ButtonClickListener implements View.OnClickListener {
  @Override
  public void onClick(View v) {
    // トーストはレイアウトに関する処理のため記述するとエラーとなる
  }
}

上記の不満は、以下のようにコンストラクタでActivityを受け取り、ButtonClickクラスのメンバ変数に格納すれば解消されますが、わざわざ外部ファイルにまでするメリットがありません。そこで、「ButtonClick」クラスにネストした匿名クラスを定義します。定義方法は次節を参照してください。

class ButtonClickListener implements View.OnClickListener {
  Activity mActivity;
  public ButtonClickListener (Activity activity) {
        mActivity = activity;
    }
  @Override
  public void onClick(View v) {
    Toast.makeText(mActivity, "ボタンがクリックされました", Toast.LENGTH_LONG).show();
  }
}

03-02-02.匿名(無名)クラスを定義する

外部クラス化の面倒を解消するために、以下のように直接setOnClickListeneメソッドにView.OnClickListenerインターフェースを実装した(無名)クラスを定義します。簡潔に書けますし、「ButtonClick」クラスのメンバ変数にもアクセスが可能です。あえてデメリットを挙げるなら、処理が長くなるとソースの可読性が下がるということでしょうか。次の匿名(無名)クラスのインスタンス化を使用すれば、若干ソースの可読性が上がるかもしれません。

button.setOnClickListener(new View.OnClickListener() {
  @Override
  public void onClick(View v) {
    Toast.makeText(this, "ボタンがクリックされました", Toast.LENGTH_LONG).show();
  });

匿名(無名)クラスのインスタンス化は、以下のように生成し、生成したインスタンスをsetOnClickListenerメソッドに定義すれば完了です。

View.OnClickListener buttonClickListener = new View.OnClickListener() {
  @Override
  public void onClick(View v) {
    Toast.makeText(this, "ボタンがクリックされました", Toast.LENGTH_LONG).show();
  }
};

匿名(無名)クラスをインスタンス化した場合のButtonClickクラスのソースは、以下のようになります。setOnClickListenerメソッド内に処理を書かないため、onCreateメソッド内がすっきりしました。

public class ButtonClick extends Activity {
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_button_click);
    Button button = (Button) findViewById(R.id.button);
    button.setOnClickListener(buttonClickListener);
  }

  View.OnClickListener buttonClickListener = new View.OnClickListener() {
    @Override
    public void onClick(View v) {
      Toast.makeText(this, "ボタンがクリックされました", Toast.LENGTH_LONG).show();
    }
  };
}

03-03.レイアウトにonClickを指定する

これは、Android 1.6で追加された機能で、レイアウトをセットするJava側にクリック用のメソッドを記述し、レイアウト側でクリック用のメソッドを呼び出すだけです。Java側に以下のようにメソッドを追加します。オーバーライドなど必要なくメソッド名も自由に指定が可能ですが、制約があります。メソッド修飾子は必ず「public」、そして引数はViewを指定する必要があります。

public void btnClick(View v) {
  Toast.makeText(this, "ボタンがクリックされました", Toast.LENGTH_LONG).show();
}

レイアウト側に「android:onClick」を追加し、Java側に定義したメソッドを指定すれば完了です。

<Button
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:text="トースト表示"
      android:id="@+id/button"
      android:onClick="btnClick" />

最後に

上記で紹介した方法は、個人で開発するならどれを使っても問題ありませんが、「03-02-02.匿名(無名)クラスを定義する」以外は、修飾子「public」のみしか指定できないため、他クラスから呼び出しが可能なため意図しない動作をする可能性があります。結局、どれを?と言うところですが、個人的に「03-02-02.匿名(無名)クラスを定義する」を使うことが多いため、「03-02-02.匿名(無名)クラスを定義する」を推しますが、結局使う側が決めることですので色々ためしてみてください。

なお、本ページでは、Buttonを例にクリック処理を行っていますが、Button以外でもイベントリスナーを設定する事が可能で、TextViewなどにも設定することも可能です。

上記以外では、Butter Knifeなどの外部ライブラリを使用しアノテーションでonClickを指定する方法などもありますが、Android標準ではないため割愛します。興味のある方はGoogle先生に尋ねてみてください。