Android Kotlin基礎講座 07.2:DiffUtilとRecycleViewのデータバインディング
目次
タスク:バインディングアダプターを作成する
このタスクでは、ビューにデータを設定するために使うバインディングアダプターを用いたデータバインディングを使えるようにアプリをアップグレードしていきます。
以前の記事では、LiveDataを受けとり、テキストビューに表示するフォーマットされたstringを生成するためにTransformationsクラスを使いました。しかしながら、別の型や、複雑な型をバインドする必要がある場合には、データバインディングがそれらの型を扱えるようにするためのバインディングアダプターというものを使うこともできます。
バインディングアダプターはデータを受け取り、それをデータバインディングがテキストや画像などのビューにバインドできるように適合させてくれるアダプターです。
これから三つのバインディングアダプターを実装していきます。一つは、質の画像用、残り二つはそれぞれのテキストフィールド用です。要約すると、バインディングアダプターを宣言するには、アイテムやビューを受け取るメソッドを宣言し、それを@BindingAdapterでアノテーションします。メソッドの中身で、transfromationを実装します。Kotlinでは、バインディングアダプターはデータを受け取るビュークラス上の拡張関数として表記することができます。
ステップ1:バインディングアダプターを作成する
このステップではいくつかのクラスをインポートする必要がありますが、都度説明することは省きます。必要に応じてインポートしてください。
- SleepNightAdapter.ktを開いてください。
- ViewHolderクラス内のbind()メソッドを見つけ、このメソッドが何をしているかを確認しておいてください。binding.sleepLength、binding.quality、binding.qualityIMage用の値を計算するコードをアダプター内で代わりに利用することになります。(ここでは一旦コードはそのままにしておきます。後のステップでコードを移動させます)
- sleeptrackerパッケージ内でBindingUtils.ktというファイルを作成して開いてください。
- TextView上でsetSleepDurationFormattedという拡張関数を宣言し、SleepNightを渡してください。この関数が睡眠時間を計算し、フォーマットするアダプターになります。
fun TextView.setSleepDurationFormatted(item: SleepNight) {}
- setSleepDurationFormatted内で、ViewHolder.bind()で行ったようにデータをビューにバインドします。convertDurationToFormatted()を呼び出し、TextViewのtextをフォーマットされたテキストに設定してください。(これはTextView上の拡張関数なおんで、textプロパティに直接アクセスできます)
text = convertDurationToFormatted(item.startTimeMilli, item.endTimeMilli, context.resources)
- データバインディングにこのバインディングアダプターについて知らせるために、この関数を@BindingAdapterでアノテーションしてください。
- この関数はsleepDurationFormatted属性用のアダプターなので、引数としてsleepDurationFormattedを@BindingAdapterに渡します。
@BindingAdapter("sleepDurationFormatted")
- 二つ目のアダプターはSleepNightオブジェクト内の値に基づいて睡眠の質を設定します。TextView上にsetSleepQualityString()という拡張関数を作成し、SleepNightを渡してください。
- この関数内で、ViewHolder.bind()で行ったようにデータをビューにバインドします。convertNumericQualityToStringを呼び出し、textを設定してください。
- この関数を@BindingAdapter(“sleepQualityString”)でアノテーションしてください。
@BindingAdapter("sleepQualityString")
fun TextView.setSleepQualityString(item: SleepNight) {
text = convertNumericQualityToString(item.sleepQuality, context.resources)
}
- 三つ目のアダプターはイメージビューに画像を設定します。ImageView上でsetSleepImageという拡張関数を作成してください。この関数内でViewHolder.bind()からのコードを以下のように利用してください。
@BindingAdapter("sleepImage")
fun ImageView.setSleepImage(item: SleepNight) {
setImageResource(when (item.sleepQuality) {
0 -> R.drawable.ic_sleep_0
1 -> R.drawable.ic_sleep_1
2 -> R.drawable.ic_sleep_2
3 -> R.drawable.ic_sleep_3
4 -> R.drawable.ic_sleep_4
5 -> R.drawable.ic_sleep_5
else -> R.drawable.ic_sleep_active
})
}
ステップ2:SleepNightAdapterを更新する
- SleepNightAdapter.ktを開いてください。
- bind()メソッド内のコードを全て削除してください。データバインディングと新しく作成したアダプターがこの役割を果たしてくれるので、もう必要ありません。
fun bind(item: SleepNight) {
}
- bind()内で、itemにsleepを代入してください。バインディングオブジェクトに新しいSleepNightについて知らせる必要があるためです。
binding.sleep = item
- その下に、binding.executePendingBindings()を追加してください。この呼び出しはデータバインディングに保留中のバインディングを直ちに行うように要求するように最適化するものです。わずかにビューのサイジングがスピードアップするので、RecyclerViewのバインディングアダプターを使うときにはexecutePendingBindings()を常に呼び出すべきです。
binding.executePendingBindings()
ステップ3:バインディングをXMLレイアウトに追加する
- list_item_sleep_night.xmlを開いてください。
- ImageView内に、画像を設定しているバインディングアダプターと同じ名前のappプロパティを追加してください。以下のように、sleep変数を渡してください。
このプロパティはアダプターを通して、ビューとバインディングオブジェクト間の接続を生成します。sleepImageが参照されたときには毎回アダプターがSleepNightからデータを適合させます。
app:sleepImage="@{sleep}"
- 同様に、sleep_lenghtとquality_stringテキストビューもそれぞれ設定してください。
app:sleepDurationFormatted="@{sleep}"
app:sleepQualityString="@{sleep}"
- アプリを起動してください。以前と全く同じように動作します。バインディングアダプターがデータが変更した際にフォーマットと更新に関する処理を全て行ってくれます。これによりViewHolderを簡素化することができ、コードの構造も以前よりもかなり改善されました。
最後のいくつかの演習では同じリストを表示してきました。Adapterインターフェースによってさまざまな方法でコードを構築することができるということを示すためです。コードが複雑になればなるほど、コードの構造をきれいにすることは重要となってきます。製品としてのアプリでは、RecyclerViewを用いたこれらのパターンや他のパターンが使われます。全てのパターンは動作しますが、それぞれに各々の利点があります。何を作るかに応じてどのパターンを使うかを適宜選択していくべきです。
おめでとうございます!これでAndroidにおけるRecyclerViewの使い方をマスターすることができました。
完成済みプロジェクト
お疲れさまでした。完成済みプロジェクトは以下からダウンロードできます。