はじめに
Data Bindingのセットアップ方法と使い方を紹介します。
「Data Binding」とは?
Googleが提供しているJetpackのコンポーネントです。
UIコンポーネントとデータソースを、コードでなく宣言的にバインドできるようになります。
環境
- OS:macOS Big Sur 11.5.2
- Android Studio:Arctic Fox | 2020.3.1 Patch 1
- Kotlin:1.5.21
- Gradle:7.0.2
- Gradle plugin:7.0.1
セットアップ
Data Bindingの有効化
appフォルダ配下の「build.gradle」でData Bindingを有効化します。
android {
+ buildFeatures {
+ dataBinding true
+ }
}
これでData Bindingのセットアップは完了です。
使い方
フラグメントでData Bindingする
以下の記事で説明したフラグメントを、View BindingからData Bindingに変更します。
レイアウトファイルの編集
Data Bindingするには、レイアウトファイルのルートタグを layout
に変更する必要があります。
忘れがちなので注意しましょう。
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.monsterdetail.MonsterDetailFragment">
<ImageView
android:id="@+id/icon_imageview"
android:layout_width="240dp"
android:layout_height="240dp"
android:layout_marginTop="56dp"
android:contentDescription="@string/icon_description"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
app:layout_constraintVertical_chainStyle="packed"
tools:src="@tools:sample/avatars" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
ルートタグを変更したら、バインドしたいプロパティを data
タグに追加します。
今回は monster
プロパティを対象とします。
class MonsterDetailFragment : Fragment() {
private val monster: MonsterItem = ○○
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
+ <data>
+
+ <variable
+ name="monster"
+ type="com.theuhooi.uhooipicbook.ui.monsterlist.entities.MonsterItem" />
+ </data>
+
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.monsterdetail.MonsterDetailFragment">
<ImageView
android:id="@+id/icon_imageview"
android:layout_width="240dp"
android:layout_height="240dp"
android:layout_marginTop="56dp"
android:contentDescription="@string/icon_description"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
app:layout_constraintVertical_chainStyle="packed"
tools:src="@tools:sample/avatars" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
ただ iconImageView
にバインドしたいのは monster.iconUrlString
であり、 ImageView
にはURLをレイアウトファイルから紐付ける機能がありません。
バインディングアダプターの実装
そこでバインディングアダプターの出番です。
レイアウトファイルでバインドできるよう、アダプターを用意します。
バインディングアダプターは util/ViewBindingAdapters.kt
のように1ファイルにまとめることが多いようです。
@BindingAdapter("imageUrl")
fun load(imageView: ImageView, imageUrl: String?) {
imageView.load(imageUrl)
}
メソッドの第1引数に対象のビュー、第2引数に渡したい値を記述します。
@BindingAdapter
アノテーションにはレイアウトファイルに記述する名前を付けます。
基本的には第2引数と同名でいいと思います。
レイアウトファイルでバインディング
アダプターを実装したら、レイアウトファイルでバインディングします。
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="monster"
type="com.theuhooi.uhooipicbook.ui.monsterlist.entities.MonsterItem" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".ui.monsterdetail.MonsterDetailFragment">
<ImageView
android:id="@+id/icon_imageview"
android:layout_width="240dp"
android:layout_height="240dp"
android:layout_marginTop="56dp"
android:contentDescription="@string/icon_description"
+ app:imageUrl="@{monster.iconUrlString}"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0"
app:layout_constraintVertical_chainStyle="packed"
tools:src="@tools:sample/avatars" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
レイアウト式は @{}
で括ることで記述できます。
補完が効くので便利です。
レイアウト式では演算子やメソッドを呼び出せますが、単体テストができないため、シンプルにすることが推奨されています。
シンプルにする手段の一つとして、先ほど実装したバインディングアダプターがあります。
コードによるバインディング処理を削除
コードによるバインディング処理は不要になったため削除します。
class MonsterDetailFragment : Fragment() {
private var _binding: MonsterDetailFragmentBinding? = null
private val binding get() = _binding!!
private val monster: MonsterItem = ○○
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
- binding.iconImageview.load(monster.iconUrlString)
}
}
おわりに
これでバインディングがレイアウトファイルのみで完結できました!
レイアウトファイルが単体テストできないことを念頭に置きつつ、有効活用していきましょう
今回は紹介しませんでしたが、双方向バインディングという機能も便利です。
私は使ったことがないので、機会があれば使いたいです。