6回にわたり続いてきた本連載も今回で最終回です。今まで解説してきた内容をふまえて、
はじめに
Kotlinは、
開発環境構築
連載第2回で紹介した開発環境の1つに、
Android Studioを起動した状態で、
KotlinでAndroid開発を始める
まずはPhone用のプロジェクトを新規作成します。<プロジェクトルート>/app/
が生成されているはずですMainActivity.
ファイルを開きます

メニューからMainActivity.
に記述されているJavaコードが自動的にKotlinコードに変換され、

次に、


最後に、/app/
ディレクトリを作成し、MainActivity.
ファイルを/app/
からそこへ移動して完了です。これでAndroid開発を始められます!
MainActivity.
をリスト1のように編集して、
public class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
"Kotlin".hello()
}
fun String.hello() {
val msg = "Hello, $this!"
Toast.makeText(this@MainActivity, msg, Toast.
LENGTH_SHORT).show()
}
}
Kotlin活用のアイデア
KotlinでAndroid開発を始めると、
関数リテラルでイベントリスナ
KotlinはJavaとの相互運用性が高いです。JavaコードをKotlinで呼び出すのがすごく簡単です。
View
クラスのメソッドsetOnClick Listener(View.
の例を考えます。ビューのクリック時のアクションを登録するこのメソッドですが、View.
というインターフェースで表現されています。このインターフェースはfun onClick(v: View): Unit
というメソッドを1つだけ持ちます。このように抽象メソッドをただ1つ持ったインターフェースを引数に取るメソッドに、(View) -> Unit
型の関数をView#setOnClickListener(View.
の引数として渡せるということです。
リスト2ではクリック時のアクションを関数リテラルで表現してリスナ登録しています。参考までにJavaで書き直したものをリスト3に示します。
button.setOnClickListener {
Log.d(TAG, "Clicked")
}
// Javaコード
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.d(TAG, "Clicked");
}
});
拡張関数で便利APIを作る
既存の型にメソッドを生やせる拡張関数がすごく便利なことは第5回で解説しました。この拡張関数を駆使してAndroid標準のAPIを使いやすく改造してみましょう。
まずはシンプルな例としてトーストを挙げます。リスト4のようなコードを書きました。この拡張関数toast
により、Context
のサブクラス内、Activity
内でトースト表示する際にはtoast("こんにちは")
と記述するだけで済みます。このような記述を得るためだけにBaseActivity
のようなスーパークラスを導入していまいがちですが、BaseActivity
を避けられます。
fun Context.toast(msg: String) {
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show()
}
もう1つ面白い例を紹介します。リスト5では、
fun SharedPreferences.edit(f: SharedPreferences.Editor.() -> Unit) {
val editor = this.edit()
editor.f()
editor.apply()
}
platformName("putLong")
fun SharedPreferences.Editor.put(pair: Pair<String, Long>) {
putLong(pair.first, pair.second)
}
platformName("putString")
fun SharedPreferences.Editor.put(pair: Pair<String, String>) {
putString(pair.first, pair.second)
}
呼び出し側のコードはどのようになるか想像できますか? すでに取得しているプリファレンスpref
に対してリスト6のようなコードが記述できるようになります。
pref edit {
put("id" to 123L)
put("name" to "たろう")
}
まずリスト5の拡張関数edit
を見てください。この拡張関数の役目は、Shared Preferences.
を実行することです。引数として受け取る関数f
の中で実際に編集が行われ、f
の呼び出しのあとにapply
を呼び出しているだけです。引数f
の型に注目してください。初めて登場する記法です。SharedPreference.
という関数型です。() -> Unit
だけであればSharedPreference.
は、f
は、SharedPreference.
なメソッドなのです!
このf
は単なる関数ではなく、editor.
のように呼び出しているのはそのためです。editor
がレシーバとなりf
を呼び出しています。
このような拡張関数、f
の型を(SharedPreference.
として、f(editor)
と呼び出せば機能としては同じことができます。しかし呼び出し側it.
のようにレシーバをf
がSharedPreference.
の拡張メソッドであることでput("id" to 123L)
のようにレシーバを省略することが可能になります。
それからリスト5の2つの拡張関数put
についてです。この2つの関数は、Pair
を引数に取ります(説明のためにわざとらしくそうしています)。クラスPair
はその名のとおり組、Pair
の第1要素をキー、put
は同名ですが、Pair<String,String
とPair<String, Long>
で引数の型が異なります。Kotlinではこれを区別できますが、platformName
アノテーションを付けて、
最後にリスト6を見てください。put
の引数を"id" to 123L
と記述しています。これはPair
リテラルです。と言うとわかりやすいですが少し違います。to
は任意の型に対する拡張関数で、Pair
インスタンスを生成します。"id" to 123L
は"id".to(123L)
ともPair("id", 123L)
とも記述できます。
ライブラリ・ツール
KotlinでのAndroid開発をより快適にしてくれるライブラリやツールを紹介します。
Kotter Knife
Kotter KnifeはView Injectionライブラリです。頻繁に登場するfindViewById
によるビューのマッピング作業から解放してくれるライブラリです。Java用のButter KnifeのKotlin版と言えます。ちなみに開発者はAndroid界隈で名高い
導入は簡単です。リスト7のようにbuild.
dependencies {
// (略)
compile 'com.jakewharton:kotterknife:0.1.0-SNAPSHOT'
}
repositories {
// (略)
maven {
url 'https://oss.sonatype.org/content/repositories/snapshots/'
}
}
導入後、MainActivity
のプロパティとして各ビュー(nameEditText
とsubmitButton)
を保持しています。onCreate
メソッド内でfindByViewId
メソッドを呼び出してビューのマッピングを行うのが通常の方法ですが、findByViewId
が登場しません。setContent View(R.
を呼び出した後すぐにsubmitButton
に対してクリックリスナを登録しています。
class MainActivity : Activity() {
val nameEditText: EditText by bindView(R.id.name_edit_text)
val submitButton: Button by bindView(R.id.submit_button)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
submitButton.setOnClickListener {
val name = nameEditText.getText().toString()
Toast.makeText(this, name, Toast.LENGTH_SHORT).show()
}
}
}
これを可能にしているのは各ビューのプロパティの宣言時にby bindView(ID)
と記述したおかげです。bindView
メソッドはKotter Knifeが提供するAPIです。プロパティ宣言のあとにby
と記述しているのはKotlinのDelegated Propertyという機能を使うためです。詳細は割愛しますが、
Anko
次に紹介するのはJetBrainsにより開発されているライブラリ、
Android開発では通常、
まずは非常に簡単な例をご覧入れましょう。リスト9では、
class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
verticalLayout {
button("Click me!") {
onClick {
toast("Hello")
}
}
}
}
}
Kotlinコードですので、
class MainActivity : Activity() {
override fun onCreate(savedInstanceState:
Bundle?) {
super.onCreate(savedInstanceState)
val button = createLayout()
button.setOnClickListener {
toast("Hello")
}
}
}
fun Activity.createLayout(): Button {
var button: Button? = null
verticalLayout {
// ボタンのテキストとしてリソースIDも指定できる
button = button(R.string.click_me) {
// ボタンのテキストサイズを指定
textSize = 24f
}.layoutParams {
// マージンを指定
margin = dip(20)
}
}
// ボタンを返す
return button ?: throw AssertionError()
}
拡張関数createLayout
で、
肝心の導入方法ですが、dependencies
に追記するだけです。とても面白いライブラリですのでぜひ使ってみてください。
compile 'org.jetbrains.anko:anko:0.6.2-15'
Kotlin Extensions for Android
最後に紹介するのはKotlin Extensions for Androidというツールです。モチベーションとしてはKotter Knifeと同じくfindViewById
の排除です。しかしKotlin Extendions for Androidはさらに一歩進めて、
簡単な具体例を示します。リスト12はactivity_
という名前のレイアウトファイルです。そして、MainActivity
の定義がリスト13です。
<RelativeLayout xmlns:android="http://schemas.
android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="@+id/helloButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello"/>
</RelativeLayout>
package com.taroid.sample
import android.app.Activity
import android.os.Bundle
import android.widget.Toast
import kotlinx.android.synthetic.activity_main.helloButton
public class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
helloButton.setOnClickListener {
Toast.makeText(this, "Hello", Toast.LENGTH_SHORT).show()
}
}
}
リスト13では、onCreate
内でいきなり登場するhelloButton
に対してクリックリスナを登録しています。Kotter Knifeのときと異なり、helloButton
はプロパティに宣言されていなければDelegated Propertyも使用されていません。このhelloButton
、MainActivity
内で使えるようにしています。自動生成されるビューの完全な名前を一般化するとkotlinx.
」
Kotlin Extensions for Androidを導入するには、
buildscript {
// (略)
dependencies {
// (略)
classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
}
}
まとめ
Android Studioにプラグインを入れるだけで、
ボタンのクリックリスナのような、
Context
を引数に取るような便利メソッドの定義の際には、Context
に対する拡張関数とすれば、
便利なライブラリ・
おわりに
全6回に及ぶKotlin入門連載、
Kotlinでみなさまのプログラミングが少しでも快適に、
本誌最新号をチェック!
Software Design 2022年9月号
2022年8月18日発売
B5判/
定価1,342円
- 第1特集
MySQL アプリ開発者の必修5科目
不意なトラブルに困らないためのRDB基礎知識 - 第2特集
「知りたい」 「使いたい」 「発信したい」 をかなえる
OSSソースコードリーディングのススメ - 特別企画
企業のシステムを支えるOSとエコシステムの全貌
[特別企画] Red Hat Enterprise Linux 9最新ガイド - 短期連載
今さら聞けないSSH
[前編] リモートログインとコマンドの実行 - 短期連載
MySQLで学ぶ文字コード
[最終回] 文字コードのハマりどころTips集 - 短期連載
新生「Ansible」 徹底解説
[4] Playbookの実行環境 (基礎編)