まだAnko使ってないの!?

  • 18
    Like
  • 0
    Comment

はいどうも、こんばんは。
Ankoの紹介をします。

Anko イズ 何

AnkoはAndroidのレイアウト構築をするためのものです。
DSL...何か決まっている問題を解決するための言語です。AnkoはAndroidのレイアウト構築だけを考えられて作られた言語です。つまり...なんで使わないの。

ちなみにこちらJetbrains製です。中身もkotlinで書かれています。

なので、もちろんKotlinの強みであるNull安全や型推論等サポートされているわけです。

導入

gradleプロジェクトなら一瞬です。
依存関係を記述します。

compile 'org.jetbrains.anko:anko-sdk15:0.8.3'

たった一行で済みます。うんうん。

ただ、新しい環境(記述時2.3)だと動きません。
僕はAndroidStudio2.0で動かしました。

凄く粗削りですがこちらにコードもおいてあります。かなり記事にする上で端折っているのでよかったら合わせて読んでください。

使おう

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical">

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:text="登録"/>

</LinearLayout>

よく見る(見ない)レイアウト構成です。
LinearLayoutの中にEditTextとButtonが入っています。

まずはこれをAnkoってみましょう。

        linearLayout {
            orientation = LinearLayout.VERTICAL

            editText().lparams(width = matchParent, height = wrapContent)
            button(R.string.subscribe) {
                gravity = Gravity.RIGHT
            }.lparams(width = wrapContent, height = wrapContent)
        }

超簡単じゃないですか。というか見たことがあると思います。ほぼxmlのままです。
xmlが書ければankoが書ける...ankoが書ければxmlが書ける...といったことが読んでもらえばわかると思います。
プロジェクトへの進言もしやすいですね。

とはいえこれだけではまぁ...使う気にはならないですよね。
もーっといろいろスマートになります。

id要らない

idが不要になります。どういうことでしょうか。
まずはActivityのほうにクリック後の処理を書きましょう。

fun subscribe(ui: AnkoContext<MainActivity>, text: String) {
    ui.doAsync {
        Thread.sleep(500)
        activityUiThreadWithContext {
            if (checkCredentials(text)) toast("Login")
        }
    }
}

登録ボタンをしてる感処理です。

    override fun createView(ui: AnkoContext<MainActivity>) = with(ui) {
        linearLayout {
            orientation = LinearLayout.VERTICAL

            val name = editText().lparams(width = matchParent, height = wrapContent)
            button(R.string.subscribe) {
                onClick {
                    ui.owner.subscribe(ui,name.text.toString())
                }
                gravity = Gravity.RIGHT
            }.lparams(width = wrapContent, height = wrapContent)
        }
    }

そして先ほど用意したレイアウトのほうに戻ります。
少しいじってありますが。まずはbuttonにOnClickが定義してあるのがわかると思います。
そしてその中でMainActivityにあるsubscribeメソッドを呼び出しているのがわかると思います。
これは普段だったら....

        findViewById(R.id.button).setOnClickListener { 
            subscribe()
        }

といったことをしなきゃいけないものだと思います。Ankoなら昔からずっと言われている冗長番長findViewByIdを消し去り、.xmlからもid属性を消すことができます。(というか.xmlが消えるんだけど)

さらに、

今までだったら...

findViewById(R.id.button).setOnClickListener { 
    subscribe((findViewById(R.id.edittext) as EditText).text.toString())
}

と書いていたものが。

もう一点先ほどのコードと変わったところがあります。

val name = editText().lparams(width = matchParent, height = wrapContent)

EditTextを変数に代入しています。これもまたidを消すことができていて、subscribeメソッドでStringを渡さなければいけないところを楽にしてくれています。
OnClickの中で

ui.owner.subscribe(ui,name.text.toString())

nameをごにょるだけでなんとかなっています。素晴らしい...

CustomViewを作る

ただこれだと少しネストが深いとうんざりしちゃいますかね?(もちろんそれは.xmlにも言えることですが)どうすれば解決するでしょう。とりあえずCustomViewにしてみましょう。

まぁとりあえずいつもの.xmlで。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:tools="http://schemas.android.com/tools"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="horizontal">

    <ImageView
        android:id="@+id/imageview"
        android:layout_width="80dp"
        android:layout_height="80dp"
        tools:src="@android:drawable/alert_dark_frame"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:padding="10dp">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/text_name"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginRight="6dp"
                tools:text="佐々木"/>

            <TextView
                android:id="@+id/text_id"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="@android:color/darker_gray"
                tools:text="\@Sasaki_YuKi"/>

            <ImageView
                android:id="@+id/image_lock"
                android:layout_width="15dp"
                android:layout_height="15dp"
                android:layout_gravity="center"
                android:src="@android:drawable/ic_lock_lock"
                android:visibility="gone"
                tools:visibility="visible"/>
        </LinearLayout>

        <TextView
            android:id="@+id/text_description"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="4dp"
            tools:text="今日も頑張って仕事をしよう。週末はおいしいものでも食べたいなぁ。"/>
    </LinearLayout>

</LinearLayout>

custom.png

こんなやつです。
Ankoで書くとこんな感じです。

    private fun init() = AnkoContext.createDelegate(this).apply {
        orientation = LinearLayout.HORIZONTAL

        image = imageView(imageResource = android.R.drawable.ic_menu_report_image) {
            layoutParams = LinearLayout.LayoutParams(dip(80), dip(80))
        }

        linearLayout {
            layoutParams = LinearLayout.LayoutParams(matchParent, wrapContent)
            orientation = LinearLayout.VERTICAL
            padding = dip(10)

            linearLayout {
                orientation = LinearLayout.HORIZONTAL

                textView(text = "佐々木") {
                }.lparams(width = wrapContent, height = wrapContent) {
                    rightMargin = dip(6)
                }
                textView(text = "@Sasaki_Yuki") {
                    textColor = android.R.color.darker_gray
                }.lparams(width = wrapContent, height = wrapContent)
                imageView {
                    setImageResource(android.R.drawable.ic_lock_lock)
                    visibility = View.VISIBLE
                }.lparams(width = dip(15), height = dip(15)) {
                    gravity = Gravity.CENTER
                }
            }.lparams(width = matchParent, height = wrapContent)
            textView(text = "今日も頑張って仕事をしよう。週末はおいしいものでも食べたいなぁ。") {
                padding = dip(4)
            }.lparams(width = matchParent, height = wrapContent)
        }
    }

(toolsに当たるものを見つけることができなかった)

Root要素がLinearLayoutのためImageViewと名前とかが入っているLinearLayoutが入っています。

@Suppress("NOTHING_TO_INLINE")
inline fun ViewManager.myLikeTweetView(theme: Int = 0) = myLikeTweetView({}, theme)

inline fun ViewManager.myLikeTweetView(init: LikeTweetView.() -> Unit, theme: Int = 0) = ankoView(::LikeTweetView, theme, init)

呼び出し口を用意して、Activity側から呼び出します。

            val name = editText().lparams(width = matchParent, height = wrapContent)
            button(R.string.subscribe) {
                onClick {
                    ui.owner.subscribe(ui,name.text.toString())
                }
                gravity = Gravity.RIGHT
            }.lparams(width = wrapContent, height = wrapContent)
            myLikeTweetView()

これでよい。

これの呼び出し口にRecyclerViewならConvertViewやらEntityを渡せば反映させることもできるようになるわけです。

終わりに

以上です。
Kotlinを使っているなら思い切ってankoにしてみるのもいいんじゃないでしょうか。
いつかより応用的な記事を書ければいいなと思います。

凄く粗削りですがこちらにコードもおいてあります。かなり記事にする上で端折っているのでよかったら合わせて読んでください。