どうも。
先日別の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の紹介を終わります。
また自分の中で使っていって面白い発見やサンプルになりそうなものがあれば書きたいと思っています。