こんにちは。PHONE APPLIでAndroidのアプリ開発を担当している田松と申します。
今回は、レイアウトとの同期がより簡単になるデータバインディングについて書きたいと思います!
データバインディングの利点
データバインディングを使用しなくとも、レイアウトとコードの値を同期させることはできますが、 同期をするためにレイアウトのViewを取得して、状態をみてから値を書き換えて〜、といった様に手順が少々面倒です。 データバインディングを使用することで、コード上の値をレイアウトに埋め込むことが出来るので、 Viewを一々取得する手間を省略出来たりするので、コード量が少なく済み、見通しが良くなったりとメリットが多いです!
データバインディングを使用するにあたって、準備は必要なので、そこから見ていきましょう!
使用するための準備
以下を gradle.app
に記述しgradleの更新を行う
android { ... dataBinding { enabled = true } }
これを記述するだけで使用することができます!
実際に使用してみる
レイアウト指定
使用に関してもとても簡単で、まずレイアウトファイルのルートタグを <layout>
タグにします。
- activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <!-- ↓ xmlの一番外側(ルートタグ)をlayoutタグにする --> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <!-- ルートタグをlayoutにした後は好きなようにlayoutを構築していく --> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/white"> <androidx.appcompat.widget.AppCompatTextView android:id="@+id/testTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Hello world" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
起動すると画面中央に,Hello Worldと表示するだけの簡単なレイアウトを作りました。
このレイアウトファイルを作成すると、自動的に ActivityMainBinding
クラスが作成されます。
次にこのレイアウトを使用するために MainActivity
を編集していきます。
- MainActivity.kt
package com.example.bindtestappli import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import androidx.databinding.DataBindingUtil import com.example.bindtestappli.databinding.ActivityMainBinding class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // setContentView(R.layout.activity_main) 本来のlayout指定方法 val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main) } }
DataBindingUtil.setContentView<{自動生成されたクラス}>(this, {対象のレイアウトクラスID})
を使用することでactivityにレイアウトを指定することが出来ます。
Fragmentでは以下のように onCreateView
にてBindingViewを返すことによりレイアウトを構築出来ます。
override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val binding = FragmentMainBinding.inflate(inflater, container, false) return binding.root }
viewの指定
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main) // 従来のfindViewByIdを使用した際 val textView = findViewById<TextView>(R.id.testTextView) textView.text = "hoge" // bindingを使用した際 binding.testTextView.text = "hoge" }
従来の findViewById
を使用した場合には一度viewを取り出す必要があったり、他のViewIdが混在するので
layout内のViewIdがそもそも何だったのかというのを把握する必要があります。
また、型指定も必要だったりでViewの取得だけでも気をつけなければいけないことも多いです。
一方bindingからviewを取得した際には、bindしたレイアウト内のViewIdのみ取得できるので、ViewIdが混在することがありません。 また、取得の際に型が決まっているので、Viewの型を改めて気にする必要ないのが良い点だと思います。
変数を利用してレイアウトに紐づける
コード内の変数や関数をレイアウトのViewに紐づけることができます。
以下のように activity_main.xml
と MainActivity
を書き換えます。
- activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <!-- ↓ xmlの一番外側(ルートタグ)をlayoutタグにする --> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto"> <data> <!-- bindするクラスを指定 --> <variable name="bind" type="com.example.bindtestappli.MainActivity" /> </data> <!-- ルートタグをlayoutにした後は好きなようにlayoutを構築していく --> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent" android:layout_height="match_parent"> <androidx.appcompat.widget.AppCompatTextView android:id="@+id/testTextView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@{ bind.testText }" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <androidx.appcompat.widget.AppCompatButton android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="button" android:onClick="@{ bind::buttonOnClick }" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/testTextView" /> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
- MainActivity.kt
package com.example.bindtestappli import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.view.View import androidx.databinding.DataBindingUtil import androidx.databinding.ObservableField import com.example.bindtestappli.databinding.ActivityMainBinding class MainActivity : AppCompatActivity() { // データバインド用変数 var testText = ObservableField<String>("hoge") override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main) // layoutの変数に格納 binding.bind = this } fun buttonOnClick(view: View) { testText.set("huga") } }
ボタンを押すとテキストを入れ替える簡単な画面を作ってみました。
レイアウト内でバインドした変数を使用する場合は、 <variable>
タグで <data>
タグを囲います。
<data>
タグ内に変数名と型を指定し、コード側で binding.bind = this
のように格納します。
関数に関してはViewを引数にすることで bind::buttonOnClick
のように使用することが出来ます。
また、 testText
変数がString型だと変数の値が変わった際にViewを自動的に再描画してくれないので、
今回詳しい説明は省きますが、 Observable
クラスを使用することで、変数の変化を監視し、
変数の値が変わった際にViewを描画し直すといったことができるようになります。
データの監視オブジェクトには Observable
クラスの他に LiveData
というものも存在しますが、
違いやメリット・デメリットを説明すると長くなるので、今回は割愛させて頂きます。
まとめ
今回はデータバインディングの基礎として、導入から基本的な使い方を紹介しました! 以前だとデータバインディングを使用するのにgradleファイルに色々追記しなくてはいけませんでしたが、 現在ではとても簡単に導入することができます。 データバインディングを活用することで、レイアウトから状態を取得して、更に処理を分けるといったことをしなくて済み、 コードの見通しが良くなるといった利点もあるので、どんどん活用していきたいですね。