[Android][Kotlin] KotlinでAndroidアプリケーション その2 [ボタンクリック]

2015.04.23

この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。

こんにちは。小室です。札幌にも春が来ました。

IMG_20150422_134522 *1

前回、Hello Worldに相当する入門的なさわりをやってみました。表示するだけではAndroidアプリケーションを記述するとは言えません。Androidアプリケーションの実装に必要な要素を1つずつ確認していきます。

OnClickListenerを実装してみる

よく利用されるであろう、ButtonをタップするとToastを表示する簡単な実装を確認してみます。前回のプロジェクトに引き続きActivity+Fragmentのシンプルなプロジェクトを利用します。Android Studioでは以下のプロジェクトを作成しておきます。

スクリーンショット 2015-04-15 9.50.45

レイアウトはこんな感じのアプリにします。実にシンプル。

device-2015-04-23-193918

レイアウトはこちら

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="New Button"
        android:id="@+id/button" />
</LinearLayout>

ActivityやFragmentに実装させる

まあ、この実装はあまり実用的ではないかもしれません。一つの画面にボタンがひとつだけというのは少々想定しづらいですし。まあ、何はともあれActivityクラスにView.OnClickListenerを実装する方法を確認してみます。

Javaの場合

public class MainJavaActivity extends Activity implements View.OnClickListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        Toast.makeText(this, "Taped", Toast.LENGTH_SHORT).show();
    }
}

ボタンがひとつだけの画面なんてそうそうないと思うので、あんまり実用的ではないですが、ActivityやFragmentクラスで内部実装を記述することはあるかと思います。OnClickListenerの実装は、MainJavaActivityクラスにされているので、button.setOnClickListener()にはthisを突っ込んであげればOKです。Kotlinの場合を確認してみます。

Kotlinの場合

public class MainActivity : ActionBarActivity(), View.OnClickListener {

    override fun onCreate(savedInstanceState: Bundle?) {
        super<ActionBarActivity>.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val button: Button = findViewById(R.id.button) as Button
        button.setOnClickListener(this)
    }

    override fun onClick(v: View?) {
        Toast.makeText(this, "Tapped", Toast.LENGTH_SHORT).show()
    }
}

ほとんど一緒のようです。
KotlinにはInterfaceクラスではなく、Traitで実現されています。そのため、継承クラスと同列にJavaのInterfaceクラスであるView.OnClickListenerを記述します。当然実装を記述する必要があるので、Javaと同じように実装をActivityクラス内に記述します。

さらに違いというところで大きいのがsuperの記述でしょうか。superクラスは通常一つですが、Traitを利用して多重継承している関係からか、superで何のクラスを指定して曖昧さを回避する必要があるようです。

定(変)数で定義させる

クラス内に変数 or 定数として定義してしまうのもよく使う手ではないでしょうか。OnClickListenerのインターフェースクラスの実装を記述した値を変数としてクラス内部に定義します。

Javaの場合

public class MainJavaActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(listener);
    }

    private View.OnClickListener listener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(MainJavaActivity.this, "Tapped", Toast.LENGTH_SHORT).show();
        }
    };
}

Kotlinの場合

public class MainActivity : ActionBarActivity() {

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

        val button: Button = findViewById(R.id.button) as Button
        button.setOnClickListener(listener)
    }

    val listener = object:View.OnClickListener {
        override fun onClick(v: View?) {
            Toast.makeText(this@MainActivity, "Tapped", Toast.LENGTH_SHORT).show()
        }
    }
}

object:という宣言を入れることで、インタフェースクラスをインスタンスとして実装ごと記述できるようです。ちなみに自分はKotlinでの、MainActivity.thisの記述方法にしばらく悩んでました。これは分からない・・・。

メソッド内部でインスタンス化させる

Javaの場合

public class MainJavaActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Button button = (Button) findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(MainJavaActivity.this, "Tapped", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

Kotlinの場合

public class MainActivity : ActionBarActivity() {

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

        val button: Button = findViewById(R.id.button) as Button
        button.setOnClickListener(object:View.OnClickListener {
            override fun onClick(v: View?) {
                Toast.makeText(this@MainActivity, "Tapped", Toast.LENGTH_SHORT).show()
            }
        })
    }
}

より簡略化

Kotlinでは、たった一つだけのメソッドを持つクラスに関しては推論が効くらしく、さらに簡略化した形で記述が可能です。

public class MainActivity : ActionBarActivity() {

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

        val button: Button = findViewById(R.id.button) as Button
        button.setOnClickListener { view ->
            Toast.makeText(this@MainActivity, "Tapped", Toast.LENGTH_SHORT).show()
        }
    }
}

単一の抽象メソッドを持つものをSAMと呼ぶようです。 *2。SAMとはSingle Abstract Methodの略です。Kotlinではラムダ式を活用してこういった記述が可能になるようです。Javaでは冗長であった余計な部分がかなり省かれました。
決まり文句となっていた、onClick(View view)などと言った記述を一切書かなくて良いのは非常に良いですね。ラムダ式の変数名をきちんと記述してあげれば、変換前と比べ可読性を落とすことなく、シンプルに分かりやすい記述になったかと思います。

これは多用していきたい。

まとめ

Androidで避けて通れないインタフェースクラスの実装についてJavaとKotlinを比べてみました。何番煎じか分かりませんが、自分で調べながら記述することで色々と気づくことは多いですね。インタフェースクラスを変数にする時のobject:this@MainActivityという記述を知る良いきっかけになりました。

だんだんとJavaでAndroidアプリケーションを記述するのが辛くなってきた今日このごろです。Kotlinの方が楽だ・・・。

参照

脚注

  1. 大通り近くのハンバーガー屋「ジャクソンビル」のチーズバーガー
  2. Qiita - SAMインターフェースとSAM変換