[Android] Kotlin AnkoでXMLを使わないレイアウト作成

kotlin-1.0

AnkoはJetBrainsが開発しているKotlin向けのUI構築用のDSL *1ライブラリです。

通常Android開発で画面を作成するにはXMLで定義しますが、Ankoを入れることによりコード上でUIを簡潔に記述できます。例えば、EditTextとButtonのシンプルな構成の場合、XMLとコードとAnkoの記述差は下記図のようになります。

記述量対比

今回はそんなAnkoを導入するところから試してみました。ライセンスはApache License2.0です。

https://github.com/Kotlin/anko

検証環境

今回は下記環境で試しています。

Android Studio 2.2.3 for MAC
minSdkVersion 19
targetSdkVersion 25
Kotlin Version 1.0.6

準備

プロジェクトの新規作成

AndroidStudioで新しくプロジェクトを作成します。

001

プロジェクト名と保存先を指定します。

002

今回はEmptyActivityにしました。

003

右下のFinishボタンを押して作成完了です。

004

Kotolinプラグインのインストール

既にKotolinを導入済みの方はこの手順は不要です。

Android Studio > Preferences... > Plugins > Browse repositories... をクリックします。

001 002

一覧からKotlinを選択しInstallボタンをクリックします。

003_1

インストールの完了後、AndroidStudioを再起動します。

Anko DSL Preview プラグインのインストール (要:環境の確認)

Ankoで作成した画面を表示するAnko DSL Previewというプラグインがあります。

003_2

こちらのプラグインですが、現時点(2017/1/5)では、新しいバージョンである、AndroidStudio 2.2.3をサポートしていません。
https://github.com/Kotlin/anko/issues/202

サポートされていない状態で、Anko DSL Previewをインストールすると'Cannot Load Project'というエラーが出ます。

anko dsl error dialog

その場合は、AndroidStudioを対応している古いバージョンに変更するか、Anko DSL Previewをアンインストールする(使うのをあきらめる)必要があります。アンインストールする場合は、 Android Studio > Preferences... > Plugins から Anko DSL Preview を選択し、Uninstallボタンをクリックしてください。今回の検証では使うのを諦めました。

anko dsl uninstall

Gradleの設定

作成したプロジェクトを開いた状態で、cmd + shift + aを押してFind Actionを開きます。 (Windowsの場合は Ctrl + Shift + A ?)
検索窓にKotlinと入力して、一覧に出てきた Configure Kotlin in Project をクリックします。

001_cmd+shift+a

Android with Gradle を選びました。

002

対象は All modules、Kotlinのバージョンは現時点でのリリース版最新の1.0.6にしました。

003

次にbuild.gradleファイルにAnkoを追加します。必ず必要なのはAnko-SDKです。Anko-SDKはminSdkVersionによって選びます。

minSdkVersion Anko-SDK
15〜18 org.jetbrains.anko:anko-sdk15
19〜20 org.jetbrains.anko:anko-sdk19
21〜22 org.jetbrains.anko:anko-sdk21
23〜 org.jetbrains.anko:anko-sdk23

また、必要に応じて以下のライブラリを追加します。

  • org.jetbrains.anko:anko-support-v4
  • org.jetbrains.anko:anko-appcompat-v7
  • org.jetbrains.anko:anko-cardview-v7
  • org.jetbrains.anko:anko-gridlayout-v7
  • org.jetbrains.anko:anko-recyclerview-v7
  • org.jetbrains.anko:anko-design
  • org.jetbrains.anko:anko-percent

今回は 'com.android.support:appcompat-v7:' しか追加していないので、Ankoでは 'org.jetbrains.anko:anko-appcompat-v7:' しか追加していません。

dependencies {
    〜 中略 〜
    // Anko SDK
    compile 'org.jetbrains.anko:anko-sdk19:0.8.3'
    // Anko libs matching support libs
    compile 'org.jetbrains.anko:anko-appcompat-v7:0.8.3'
 }

※ 最新は0.9.1でしたが、エラーが出てしまったために0.8.3にしました。

build.gradle

Kotlinにコンバートする

MainActivityを選択し、 Code > Convert Java File to Kotlin File を選択します。

001 2

パスがjavaになっていたのでkotlinに変更しました。

002_refacter rename

コンバートが成功したかどうか確かめるために一度、シミュレーターで起動します。

003

エラーが出ずに起動されれば準備は完了です。

004

実装サンプル

MainActivityのonCreate()内の setContentView(R.layout.activity_main) を削除し、verticalLayout { … } 部分を追加しました。

package jp.classmethod.kotlinankosample

import android.app.Activity
import android.os.Bundle
import org.jetbrains.anko.*

class MainActivity : Activity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
		
        verticalLayout {
            textView("Hello world")
            button("Push")
        }
    }
}

上記を実行すると下記のような表示になります。

sample1

また、UI部分をクラスにすることも出来ます。 AnkoComponent interfaceを実装したUIクラスを作成します。下記コードでは、class MainActivityUi: AnkoComponent { ... } 部分になります。

package jp.classmethod.kotlinankosample

import android.app.Activity
import android.os.Bundle
import android.support.v4.content.ContextCompat
import android.view.Gravity
import android.text.InputType.TYPE_CLASS_TEXT
import android.text.InputType.TYPE_TEXT_VARIATION_PASSWORD
import org.jetbrains.anko.*

class MainActivity : Activity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        
        MainActivityUi().setContentView(this)
    }

    fun tryLogin(ui: AnkoContext<MainActivity>, name: CharSequence?, password: CharSequence?) {
        ui.async() {
            Thread.sleep(500)

            activityUiThreadWithContext {
                if (checkInputText(name.toString(), password.toString())) {
                    toast("ようこそ!")
                } else {
                    toast("エラー:入力してください")
                }
            }
        }
    }
    
    private fun checkInputText(name: String, password: String) = name.length != 0 && password.length != 0is)
}

class MainActivityUi: AnkoComponent<MainActivity> {

    override fun createView(ui: AnkoContext<MainActivity>) = with(ui) {
        verticalLayout {
            padding = dip(32)

            imageView(android.R.drawable.ic_menu_edit).lparams {
                margin = dip(16)
                gravity = Gravity.CENTER
            }

            val name = editText {
                hint = "名前を入れて下さい"
            }
            val password = editText {
                hint = "パスワードを入れて下さい"
                inputType = TYPE_CLASS_TEXT or TYPE_TEXT_VARIATION_PASSWORD
            }

            button("Log in") {
                lparams(width = dip(160)) {
                    topMargin = dip(32)
                    gravity = Gravity.CENTER
                }
                textColor = ContextCompat.getColor(ctx, R.color.white)
                backgroundColor = ContextCompat.getColor(ctx, R.color.skyBlue)
                onClick {
                    ui.owner.tryLogin(ui, name.text, password.text)
                }
            }
        }
    }
}

Screenshot_1483597030

さいごに

最初は見慣れなくて戸惑いましたが、見慣れてきたら直感的に書ける気がしてきました。まだ 今回はじめてKotlinをさわってみたため記載内容に気になる箇所がありましたら、ご指摘頂ければ幸いです。

参考

脚注

  1. DSLとは? Wikipedia