AndroidStudio リスナクラスとは?用語の解説から2つのボタンの条件分岐まで


記事の内容

Androidアプリのボタンをタップした時の処理や、設定方法について初心者がまとめていきます。

目次
●基本的な用語の説明
 ・イベント
 ・イベントハンドラ
 ・リスナ
●リスナ設定手順
●ボタンが2つの時の条件分岐
●サンプルコード

基本的な用語

イベント
ユーザが画面に対して行う操作 (タップ、スライド etc)
MVCモデルでは、View に対して行う操作

イベントハンドラ
イベントに対して行う処理(タップやスライドしたときに行う処理)
MVCモデルでは、Controller に近い機能

リスナ
イベントの検出。
わかりやすく言うと、イベントが発生するかどうかのセンサーの役割。
このクラスがないと何も始まらない。
MVCモデルでは対応するところなし


参照

リスナ設定手順

1,それぞれのイベントに対応したリスナクラスを作成する。
2,リスナクラス内の所定のメソッドに処理を記述する。
3,リスナクラスのインスタンスを生成してリスナ設定メソッドの引数として渡す。

1,それぞれのイベントに対応したリスナクラスを作成する。

∟それぞれのイベントとは、ボタンをタップする/ドラッグする/画面を長押しするなどといった操作のことを言います。

では、どうやってリスナクラスを作成するかと言うと、
そもそもどういったリスナクラスを作るのか(例えば、ボタンをタップする/ドラッグする/画面を長押しする)を考える
→ほとんどのイベントに対応したリスナインターフェイスが元々Androidの方で定義されている。
つまり、それを使う。

どこにあるか?
→Viewクラスのメンバインターフェイスとして定義されている。こちら
→今回のサンプルだと「ボタンをタップ」した際のリスナなので、「OnClickListenerインターフェイス」を使用する
→OnClickListenerインターフェイスを実装する

ここで重要なのは、
リスナを設定したい場合は、インターフェイスの実装が必要不可欠という事です。

2,リスナクラス内の所定のメソッドに処理を記述する。

1,でリスナクラスを作成できたので、実際に行いたい処理を記述していく。

今回は「入力された名前を取得して、ボタンタップ後に同一画面上に表示させる」という処理を行いたいです。
→そこでアクティビティ内で画面部品を取得する必要がある。
(自分は勝手に箱を取ってくるイメージを持っています。)
∟findViewById()メソッドを使う
→画面部品を取得したら文字列を取得
(自分は勝手に箱の中身を取ってくるイメージ)
∟ここで注意することは、プロパティ(クラス内で宣言された変数)のを意識する。
∟今回だと、EditTextクラス(名前の入力欄)のtextプロパティを使って入力文字列情報を取得しようとしていますが、textプロパティは、Editable型なので、toString()でString型にキャスト(型変換)してあげる必要がある。

ここで重要なことは、
文字列情報などを取得する前に、まずはアクティビティ内で画面部品を取得する必要がある。
文字列などを取得する際は、プロパティの型に注意をする。

3,リスナクラスのインスタンスを生成してリスナ設定メソッドの引数として渡す。

Kotlinは、Javaと同様に、クラスを作ったところで、インスタンス(実装)しないと使えません。
→どのボタンをタップした時に2で設定した処理を設定するかを決める
→ボタンのオブジェクトを取得
∟2で行ったように、FindViewById()で取得
→そのボタンに持たせたい処理、つまり、リスナクラスのインスタンスを生成する。
→最後に、生成したインスタンスをボタンのオブジェクトに設定してあげる。
∟リスナ設定メソッドは、設定するリスナによって異なるが、実装したインターフェイスと関連がある名前になっている。

ここで重要なことは、
クラスを作るだけでは意味がなく、インスタンス生成をして初めて使うことができるということを理解する。
どのボタンに1,2で設定した処理を持たせたいかを意識する。

ボタンが2つの時の条件分岐

先ほどまでで、1つのボタンにおけるリスナの設定を行いました。

ここでは、もう1つのボタンが追加された場合の処理について説明していきます。

やりたいこと
「表示」ボタンが押された時と、「クリア」ボタンが押された時で違うイベントハンドラを設定したい。

手順
基本的にやることはリスナ設定手順と変わりません。
1番簡単なのは、リスナ設定手順と同様に、「クリア」ボタン用のリスナクラスを設定することでもできますが、
ここでは、条件分岐処理を行っていきます。
(ボタンが増えた時にそっちの方が効率的だからです。)

onClick()メソッドの引数であるviewでどちらのボタンをクリックしたかどうかの条件分岐を作る
→どうやってやるか?
→Viewクラスにはidプロパティがあり、画面部品のidのR値を取得出来きます。
→そのR値が「表示」か「クリア」かどうかをwhen(view.id)の条件に当てはめる。
→ここまできたら、あとは、リスナ設定手順で解説した、それぞれのイベントハンドラを設定するコードを記述する。

ここで重要なことは、
条件分岐を設定する時に、
何で判断するか?(今回だったら、idとR値)を意識してコードを書きます。

サンプルコード

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

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/tv_name"/>

    <EditText
        android:id="@+id/etName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:inputType="text"/>

    <Button
        android:id="@+id/btClick"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/bt_click"/>

    <Button
        android:id="@+id/btClear"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/bt_clear"/>

    <TextView
        android:id="@+id/tvOutput"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="25dp"
        android:text=""
        android:textSize="25sp"/>

</LinearLayout>
MainActiviity.kt
package com.websarva.wings.android.hellosample

import android.annotation.SuppressLint
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        //表示ボタンであるButtonオブジェクトを取得。
        val btClick = findViewById<Button>(R.id.btClick)
        //リスナクラスのインスタンスを生成
        val listenr = HelloLisner()
        //表示ボタンにリスナを設定
        btClick.setOnClickListener(listenr)

        //クリアボタンであるButtonオブジェクトを取得
        val btClear = findViewById<Button>(R.id.btClear)
        //クリアボタンにリスナを設定。
        btClear.setOnClickListener(listenr)
    }

    //ボタンをクリックした時のリスナクラス
    //リスナクラスはインターフェイスを実装する
    private inner class HelloLisner : View.OnClickListener {
        @SuppressLint("SetTextI18n")
        override fun onClick(view: View) {
            //名前入力欄であるEditTextオブジェクトを取得。
            val input = findViewById<EditText>(R.id.etName)
            //メッセージを表示するTextViewオブジェクトを取得。
            val output = findViewById<TextView>(R.id.tvOutput)

            //idのR値に応じて処理を分岐。
            when(view.id) {
                //表示ボタンの場合...
                R.id.btClick -> {
                    //入力された名前文字列を取得。
                    val inputStr = input.text.toString()
                    //メッセージを表示。
                    output.text = inputStr + "さん、こんにちは!"
                }
                //クリアボタンの場合...
                R.id.btClear -> {
                    //名前入力欄を空文字に設定。
                    input.setText("")
                    //メッセージ入力欄を空文字に設定。
                    output.text =""
                }
            }
        }
    }
}

オブジェクト式とラムダ式

Kotlinでは、オブジェクト式という記述方法がある。

サンプルコード

Sample.kt
btClick.setOnClickListener(object: View.onClickListener{
    overrride fun onClick(view: View) {
        :
    }
})

View.onClickListenerインターフェイスのように、メソッドを1つしか持たないインターフェイスの場合は、ラムダ式を使って以下のように書き換える事が出来る。

Sample.kt
btClick.setOnClickListener{view: View ->
    :
}

ラムダ式を使用することで、コード数を減らすことが出来るが、可読性が下がってしまう。

参考
https://memodays.jp/241/