前提
KotlinでData Bindingを行わずListViewが表示されている状態からはじめます。
はじめの準備はこちらを参照してください。各種モデルクラスも記載されています。
準備
今回は複数個のBookインスタンスをListViewで表示します。
バインドするため、BookShelf
クラスを作成します。
BookShelf.kt
class BookShelf(private val books: List<Book>) {
val count: Int = books.count()
fun bookAt(index: Int): Book {
return books[index]
}
}
リストアイテムのレイアウトを編集する
バインドはListViewの各アイテムについて行います。
基本編と同じように下記をリストアイテムのレイアウトについても行います。
ListViewの1アイテムの定義なので、バインドするクラスは1つの本を表すBook
です。
- ListViewのアイテム用のレイアウトファイルを作成
- Layoutタグで括る
- dataタグを追加する
- バインドの定義をする
book_item.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="book"
type="com.example.kotlinsample.model.Book" />
</data>
<LinearLayout ...>
<TextView
...
android:text="@{book.title}"
android:id="@+id/titleText"
... />
<TextView
android:text="@{String.valueOf(book.price)}"
android:id="@+id/authorText"
... />
</LinearLayout>
</layout>
データアダプタを用意する
ポイントは、以下のとおりです。
- ListViewに表示するアイテムのレイアウトクラスに対応したBindingクラス(
BookItemBinding
クラス)が自動生成されているので、それを使う -
getView
でconvertView
が-
null
の時にBookItemBinding
クラスを作成 - それ以外の時は使い回す
- Bindingクラスのinflateメソッドが遅いので、使いまわさないと速度が落ちる
-
-
getView
の戻りはBookItemBinding
のインスタンスのroot
することでViewが返る - ViewModelを設定するのを忘れずに!
また、bindingはアダプターのメンバ変数として定義し、nullである場合のみインスタンス化してメンバ変数に代入する方法でも良いと思います。
ListBindingAdapter.kt
class ListBindingAdapter(val context: Context, val bookShelf: BookShelf): BaseAdapter() {
var inflater: LayoutInflater
val books = bookShelf
init {
inflater = LayoutInflater.from(context)
}
override fun getCount(): Int {
return books.count
}
override fun getView(position: Int, convertView: View?, parent: ViewGroup?): View? {
var binding: BookItemBinding?
if (convertView == null) {
binding = BookItemBinding.inflate(inflater, parent, false)
binding.root.tag = binding
} else {
binding = convertView.tag as BookItemBinding
}
binding?.book = getItem(position) as Book
return binding?.root
}
override fun getItem(position: Int): Any? {
return bookShelf.bookAt(position)
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
}
データアダプターをListViewに対してセットすれば完了
こんな感じで普通の場合と同じ
ListViewSampleActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_list_binding)
val listView = findViewById(R.id.bookShelf) as ListView
// 適当にデータを作成
val bookList = mutableListOf<Book>()
for (i in 0..100) {
bookList.add(Book(
title = "漫画 ($i)巻",
price = i,
authors = listOf<Author>()
))
}
val bookShelf = BookShelf(books = bookList)
listView.adapter = ListBindingAdapter(this, bookShelf)
}
最後に
- Bindingクラスの使い回しのベストプラクティスが不明
- tagに入れているがイマイチ
- メンバ変数で保持する方が良いか
そんなに通常のViewとやること変わらないですね。