どうも。
先日別のadventCalendarにてまだAnko使ってないの!?
というタイトルでAnko
の説明を軽くしました。
もしよかったらチェックしてみて下さい。
読んでいただいた方にはAnkoの魅力が伝わっていればいいなと思います。
そして、もっと紹介してくれ、という要望(?)にお答えして
今日はさらにAnko
の魅力を紹介するために少し応用的な使い方をしていきます。
kotlin
的な文法もかなり使っていくのでkotlin
の魅力にも気づいていただけたらと思います。
非同期処理
Anko
を使っているとすごく簡単にバックスレッドに移動、UI
スレッドに移動することができます。
まずはJava
コードならHandle
使ったりAsync
でなんかしたりといろいろあると思いますが、冗長な部分が多く、しかも匿名メソッドなので全体の見通しが悪くなりがちです。
ui.doAsync {
Thread.sleep(500)
activityUiThreadWithContext {
if (checkCredentials(text)) toast("Login")
else toast("error")
}
}
前回の記事中のMainActivity
内で行っていた処理でもあります。何気なく記述しましたがAnko
の恩恵を2点受け取っています。
まずはこの項目である、非同期処理
ui
はAnkoContext
です。それにたいしてdoAsync
を行うだけです。別スレッドで順次処理されていきます。Runnnable
と記述が似ています。
Toast
show
忘れたり引数が多いToast
ですが上記のように一文で記述できます。
またリソースIdだけ渡しても問題ありません。Longを出す場合は下記です。
longToast("長いToastを出したい")
簡単でしょう。
ログ
デバグ中によく使うLog.x
特にその中でもd
やe
は使うのではないでしょうか。
if (checkCredentials(text)) debug("login success")
else error("login error")
明示的に何をしているのか、さらに簡単に記述。
AnkoLogger
を継承する必要があります。
class MainActivity : AppCompatActivity(),AnkoLogger {
AlertDialog
DialogManager
を呼び出して~インスタンス化して~PositiveButton
のテキストと無名ClickListener実装して~引数多くて見づらいし~ともうネスト深くなりまくる地獄。
なんとかラップして見やすくしたけどコールバックが~とわりと鬱陶しいやつですがAnkoなら瞬殺です。
alert("タイトルです", "メッセージです") {
yesButton {}
noButton {}
}.show()
残念ながらリソースIDではだめです。ちょっと惜しいな。
しかし、見通しはかなりよくなりました。
ちなみにこんなこともできます。
alert {
customView {
editText {
hint = "こうなったら逃げられまい"
}
}
yesButton {}
noButton {}
}.show()
その場でCustomViewを作り上げ適用。なんてことだ、なぜ使ってなかったんだ今までってなったらもう一度僕の1個目の記事を読み直してPMに早く打診しに行くんだよ!!
ListAdapterを作ってみる
他にも便利機能はあるのですが少しアプローチを変えて、Anko
で普段使いそうなものを書いてみます。
タイトル通りListAdapter
を作ります。汎用的なやつです。
まずは前回同様Listに表示するアイテムを作ります。もちろん.xml
は使いません。最も簡単な作りのものでこちらを継承して見た目を変えていくことにします。
コードを上げておくので詳しいものはそちらを見てください。こちらの記事では端折ったものを載せておきます。
こちら!!!!
ListAdapterと同ファイル(ここはなんでも。個人的にはせっかくAnko
で書いてるのでレイアウトも同じファイルにしたいと思っています。)に記述。ベースになるView
です。
interface ListItem : AnkoComponent<AnkoListAdapter> {
fun apply(convertView: View)
}
internal open class TextListItem(val text: String) : ListItem {
protected inline fun createTextView(ui: AnkoContext<AnkoListAdapter>, init: TextView.() -> Unit) = ui.apply {
textView {
id = android.R.id.text1
init()
}
}.view
override fun createView(ui: AnkoContext<AnkoListAdapter>) = createTextView(ui) {
gravity = Gravity.CENTER_VERTICAL
padding = ui.dip(20)
textSize = 13f
}
private fun getHolder(convertView: View): Holder {
return (convertView.tag as? Holder) ?: Holder(convertView as TextView).apply {
convertView.tag = this
}
}
override fun apply(convertView: View) {
val h = getHolder(convertView)
h.textView.text = text
}
internal class Holder(val textView: TextView)
}
ここでのポイントはView
の内部ではなくListner
です。AnkoComponent<AnkoListAdapter>
を実装しています。このapplyでListAdapterを繋がっています。このような実装になることで依存関係がなくなるのでBaseがTextListItemであればレイアウトは自由自在というわけです。今回の例ではTextView
ですが、LineaLayout
で作ればもっと柔軟なものが出来上がるでしょう。
肝心のadapterですが、特に難しいことはしてません。というかいつもと同じです。前述通り今作ったViewとのつながりはapplyでそれをgetView
の中で呼んでいるくらいです。
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View? {
val item = getItem(position)
if (item != null) {
val view = convertView ?: item.createView(ankoContext)
item.apply(view)
return view
} else return convertView
}
これでBaseのListAdapter
が完成しました。これだけだと味気ないので、よく実装するセクション区切りも実装できるようにしたいと思います。
よく必要になるセクション区切りをつけます。
val items = listOf(
"地球連邦軍" to listOf("ジム", "ガンキャノン", "陸戦型ガンダム"),
"ジオン公国軍" to listOf("ザク", "ギャン", "イフリート改"))
具体的にはこのリストを表示します。
それぞれxx軍をセクションヘッダーにします。
ではヘッダーとなるレイアウトを作成します。
internal class LitItem(text: String = "") : TextListItem(text)
internal class SectionHeaderItem(text: String = "") : TextListItem(text) {
override fun createView(ui: AnkoContext<AnkoListAdapter>) = createTextView(ui) {
gravity = Gravity.CENTER_VERTICAL
padding = dip(10)
textColor = Color.BLUE
backgroundColor = Color.GRAY
textSize = 17f
}
}
通常にListItem
はこれで先ほど実装したTextListItem
のままでオッケー(という判断)
セクションヘッダーはそのままだと困るので新たにViewを作ります。継承して実装。バックグラウンドにカラーを付けて文字サイズを大きくしてみました。よさそう。
protected abstract val listItems: List<Class<out ListItem>>
out
修飾子がついているので型安全にListItemに移行?することができます。
作ったviewをlistItemsに渡します。
internal class MSListAdapter(context: Context, items: List<ListItem>) : AnkoListAdapter(context, items) {
override val listItems = listOf(SectionHeaderItem::class.java, LitItem::class.java)
}
おっけー。
あとはこのadapterをセットするだけで表示されるわけです。
CustomView
の実装が非常に簡単かつ横にいるので非常に見通しがいいです。
また、Javaではできなかった簡単なコレクション操作がkotlinのおかげでできるので見通しもいいわけです。
終わりに
本日のサンプルコードはこちらにあります。
こちら!!!!
以上でAnkoの紹介を終わります。
また自分の中で使っていって面白い発見やサンプルになりそうなものがあれば書きたいと思っています。