LoginSignup
4
6

More than 5 years have passed since last update.

Kotlin向けAndroid UI構築用ライブラリAnko Wiki要約

Last updated at Posted at 2017-10-19

Understanding Ankoをほぼ日本語訳しただけのものですが、ご了承ください。
※また、私自身、英語力には自信がありませんので、誤りがありましたらご指摘ください。

基本

Ankoライブラリを利用するために、org.jetbrains.anko.*パッケージをインポートしましょう。

それでは、onCreate()の例を見てみましょう。

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    verticalLayout {
        padding = dip(30)
        editText {
            hint = "Name"
            textSize = 24f
        }
        editText {
            hint = "Password"
            textSize = 24f
        }
        button("Login") {
            textSize = 26f
        }
    }
}

Ankoでは、このようなDSLで記述できます。従来のように、setContentView(R.layout.something)を呼び出す必要はありません。
これを詳しく見れば、

  • hint, textSize: JavaBeansのgetter/setter仕様に乗っ取った拡張プロパティ
  • padding: Ankoの拡張プロパティ
  • verticalLayout, editText, button : Viewインスタンスを生成し、親Viewに追加する拡張関数

であることがわかります。

このような記法は、Androidフレームワーク下のほぼ全てのViewに採用され、Activity, Fragment, Context上で動作します。

AnkoComponent

前項では、onCreate()内にDSLを記述しましたが、通常は別のクラスを作ったほうが良いでしょう。
以下のように、onCreate()内で、AnkoComponentインターフェースを実装したクラスのインスタンスのsetContentView()を呼び出しましょう。

class MyActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
        super.onCreate(savedInstanceState, persistentState)
        MyActivityUI().setContentView(this)
    }
}

class MyActivityUI : AnkoComponent<MyActivity> {
    override fun createView(ui: AnkoContext<MyActivity>) = with(ui) {
        verticalLayout {
            val name = editText()
            button("Say Hello") {
                onClick { ctx.toast("Hello, ${name.text}!") }
            }
        }
    }
}

ブロックの省略

もしViewにプロパティが不要であれば、{}を省略して、button("Ok")button()とできます。

例:

verticalLayout {
    button("Ok")
    button(R.string.cancel)
}

また、以下のように、テンプレートを付加することもできます。

verticalLayout {
    themedButton("Ok", theme = R.style.myTheme)
}

※Themed blocksを利用し次第、書き改めます。

レイアウトの記述

レイアウトを記述する際に、従来では、XML内に、

<ImageView 
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="5dip"
    android:layout_marginTop="10dip"
    android:src="@drawable/something" />

のように記述していましたが、Ankoでは、Viewの後ろにlparams()を続けて、

linearLayout {
    button("Login") {
        textSize = 26f
    }.lparams(width = wrapContent) {
        horizontalMargin = dip(5)
        topMargin = dip(10)
    }
}

のように記述できます。widthheightは、lparams()の名前付き引数として定義されており、省略した場合は、いずれもwrapContextのデフォルト値が使用されます。
また、

  • horizontalMargin : 左右
  • verticalMargin : 上下
  • margin : 上下左右

のプロパティを用いることで、対応する余白が同時に指定できます。

lparams()の記述はレイアウト(ViewGroup)によって異なります。例えば、RelativeLayoutであれば、

val ID_OK = 1

relativeLayout {
    button("Ok") {
        id = ID_OK
    }.lparams { alignParentTop() }

    button("Cancel").lparams { below(ID_OK) }
}

となります。

Listeners

ボタンを作成する際、

button("Login") {
    onClick {
        // do something
    }
}

と記述していました。実はこれ、

button.setOnClickListener(object : OnClickListener {
    override fun onClick(v: View) {
        launch(UI) {
            // do something
        }
    }
})

とほぼ同じ記述なのです。つまり、拡張関数内でコルーチンが作成されるため、非同期処理をシームレスに記述することができるのです。

Ankoには、他にも可読性を向上させる記述方法があります。例えば、

seekBar.setOnSeekBarChangeListener(object : OnSeekBarChangeListener {
    override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
        // Something
    }
    override fun onStartTrackingTouch(seekBar: SeekBar?) {
        // Just an empty method
    }
    override fun onStopTrackingTouch(seekBar: SeekBar) {
        // Another empty method
    }
})

を現在のAnkoでは、

seekBar {
    onSeekBarChangeListener {
        onProgressChanged { seekBar, progress, fromUser ->
            // Something
        }
    }
}

と記述できます。onProgressChanged()onStartTrackingTouch()を同じView内に記述することも可能です。同じ関数を呼び出した場合は、最後に呼び出したものが適用されます。

また、以下のように、自前のコルーチンコンテキストを利用することもできます。

button("Login") {
    onClick(yourContext) {
        val user = myRetrofitService.getUser().await()
        showUser(user)
    }
}

リソースの扱い

前項までの例では、文字列をそのまま用いていましたが、本来ならば、文字列データはres/values/ディレクトリ以下にあり、実行時に呼ばれるべきです。
もちろんAnkoでも、今まで、getString(R.string.login)と記述していたように、
button(R.string.login)

button { textResource = R.string.login }
と記述することが可能です。ここで注意しなければならないのが、text, hint, imageではなく、textResource, hintResource, imageResourceを用いる必要があるということです。

インスタンスの取得

Ankoでは、ctxと記述するだけで、Contextインスタンスを取得できます。インナークラスであっても、this@SomeActivityと記述する必要はありません。また、Activityインスタンスはactで取得できます。

DSL内でXMLを利用する

include()を用いて、

include<View>(R.layout.something) {
    backgroundColor = Color.RED
}.lparams(width = matchParent) { margin = dip(12) }

と記述することで、簡単にXMLをDSLに挿入できます。
lparams()はいつでも利用でき、View以外の特定の型を用いる場合も同じ記述ができます。

例:

include<TextView>(R.layout.textfield) {
    text = "Hello, world!"
}

参考

4
6
0

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
4
6