8
6

More than 3 years have passed since last update.

[Android]思考停止で使っていたsetOnClickListener{}を少し掘り下げる

Last updated at Posted at 2021-05-24

ボタンをポチポチ

どうもReoです。
Buttonをタップした時に、何かの処理をした時に、取り敢えず

buttonView.setOnClickListener {
    // 何かしらの処理を書く
}

こんな感じで当たり前のように使っていました。
しかし、最近これの拡張関数を作る時にググって参考にしたメソッドがあるのですが、それが理解できない。
何故なら、リスナーがどう成り立っているのか分からなかったから。

さて、こいつを思考停止で使っていたことが判明したので、掘り下げたいと思います。

裏のコード旅へ!

適当にButtonViewを追加して、MainActivityとかからViewを呼び出して、setOnClickListenerを実装してください。
裏のコード見るには、リスナーメソッドにカーソルを合わせてWindowsの方はCtrl + Enter。
Macの方は、Command + Enterです。

こんなコードに飛んだかと思います。

View.java
   public void setOnClickListener(@Nullable OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        getListenerInfo().mOnClickListener = l;
    }

まず、引数はコールバック関数とか言われる引数に関数をセットしているものですね。
@Nullableというアノテーションがつくことで、ヌル許容になっているようです。

では、今度はOnClickListenerに飛んでみましょう!

View.java
    /**
     * Interface definition for a callback to be invoked when a view is clicked.
     */
    public interface OnClickListener {
        /**
         * Called when a view has been clicked.
         *
         * @param v The view that was clicked.
         */
        void onClick(View v);
    }

ここでは、OnClickListenerというインターフェースを定義しています。
インターフェースとは、具体的処理が書かれていない関数を持つクラスみたいなものです。
こちらの関数をsetOnClickListenerの引数にセットしているわけです。
続いて、isClickableへ。

View.java
/**
     * Enables or disables click events for this view. When a view
     * is clickable it will change its state to "pressed" on every click.
     * Subclasses should set the view clickable to visually react to
     * user's clicks.
     *
     * @param clickable true to make the view clickable, false otherwise
     *
     * @see #isClickable()
     * @attr ref android.R.styleable#View_clickable
     */
    public void setClickable(boolean clickable) {
        setFlags(clickable ? CLICKABLE : 0, CLICKABLE);
    }

ここで、クリックイベントの制御を行っています。
boolean型を引数に取るのと、後は単純にコメントの一行目からも推測できますね。

View.java
  getListenerInfo().mOnClickListener = l;

これは、まぁ引数に渡したリスナーの情報を取得するなにかでしょう・・・
と、と、とりあえず、setOnClickListenerに渡す引数がOnClickListenerのインターフェースであり、内部ではクリックイベントの制御等が行われていることがわかれば大丈夫でしょう。

あれれ?引数は???

さぁ、ここで疑問に思った方もいるかもしれません。(自分は思いました)

buttonView.setOnClickListener {
    // 何かしらの処理を書く
}

このようにして、setOnClickListenerを利用しますが・・・・・

引数どこなん????

おかしいですよね。()内にセットするはずのものがここでは書かれていません。
しかし、正常にリスナーが処理を行ってくれます。

実は、これSAM変換というものが行われています。
SAMとは、Single Abstract Methodの略で、一つのメソッドしかもたない抽象クラスのことです。

これによる恩恵は、メソッドを一つしか持たないinterfaceならばラムダに変換できることです。
ちなみに、普段使用しているhogeView.setOnClickListener{}のちゃんとした(?)書き方は、こうです。

hogeView.setOnClickListener(object: View.OnClickListener {
     override fun onClick(view: View?): Unit {
          // 何らかの処理
   }
})

これが、本来の書き方です。
これをSAM変換することによって、我々は可読性が良い書き方をできているわけです。
ちなみに、objectはインターフェースをインスタンス化できるもので、
Unitは、void、つまり返り値がないことを意味しています。
onClickの定義を見返すと、voidであることがわかると思います!

SAM変換の流れを詳しく解説している記事がありましたので、こちらを参考ください。

終わりに

当たり前のように、ボタンポチポチのイベント処理を書いていたけど、見えないところで使いやすい工夫が施されていたとは・・・・
これからも、思考停止シリーズを続けていきたいと思います。

8
6
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
6