PHONE APPLI Engineer blog

エンジニアブログ

Androidでデータバインディング活用

こんにちは。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.xmlMainActivity を書き換えます。

  • 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ファイルに色々追記しなくてはいけませんでしたが、 現在ではとても簡単に導入することができます。 データバインディングを活用することで、レイアウトから状態を取得して、更に処理を分けるといったことをしなくて済み、 コードの見通しが良くなるといった利点もあるので、どんどん活用していきたいですね。


PHONE APPLIについて

phoneappli.net
phoneappli.net