Help us understand the problem. What is going on with this article?

Introduction of Data Binding in Android

本稿ではData bindingの実装手順を備忘録として残しておきます。

SeekBarとViewModel、TextViewを結びつけて、SeekBarの変化を画面表示に反映する機能を実装します。

使用する主な道具としては、SeekBarData BindingLiveDataになります。

Githubに実装がおいてあります。説明上コードを適宜省略していますので、詳しくはこちらのレポジトリを参照してください。また、説明と対応するコミットへのリンクを掲載していますので、実装を追記する位置はこちらを参照してください。

環境

macOS 10.15.3

Android Studio 3.5.1

Kotlin plugin 1.3.61-release-Studio3.5-1

実装手順

以降、package名を example.android.android_data_binding_sampleとして進めます。

ビルド設定

新規プロジェクトをEmpty Activityを選んで作ったら、まず、app/build.gradleでData Binding用のビルド設定を行います。

build.gradle(app)
android {
    ...
    dataBinding {
        enabled = true
    }
}

対応するコミット

ViewModel

続いて、ViewModelを実装します。

MainViewModel.kt
class MainViewModel : ViewModel() {
    /**
     * シークバーの値を格納する
     * */
    private val _seekBarValue = MutableLiveData(50)

    /**
     * シークバーの値を表示用に整形した文字列
     * */
    val seekBarValueString: LiveData<String> = Transformations.map(_seekBarValue) {
        "value: $it"
    }

    /**
     * シークバーの値が変化した時に呼び出される
     * */
    fun onSeekBarValueChanged(value: Int) {
        _seekBarValue.postValue(value)
    }
}

対応するコミット

MutableLiveDataのイニシャライザなどでビルドエラーになる場合は、app/build.gradleにライブラリの依存関係を追加します。

build.gradle(app)
dependencies {
    ...
    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
    ...
}

対応するコミット

レイアウトファイル

次に、activity_main.xmlを編集します。

<layout>タグで既存の記述を囲みます。

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout>
    <androidx.constraintlayout.widget.ConstraintLayout
        ...
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

対応するコミット

先程実装したViewModelの宣言を行います。

activity_main.xml
<layout>

    <data>

        <variable
            name="viewModel"
            type="example.android.android_data_binding_sample.MainViewModel" />
    </data>

...
</layout>

対応するコミット

最後に、SeekBarと、SeekBarの値を表示するTextViewを宣言します。ついでにConstraintLayoutからLinearLayoutに変更します。

activity_main.xml
<layout xmlns:android="http://schemas.android.com/apk/res/android">
...
    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:orientation="vertical">

        <SeekBar
            android:id="@+id/seekBar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginBottom="20dp"
            android:progress="50" />

        <TextView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:text="@{viewModel.seekBarValueString}"
            android:textAlignment="center" />

    </LinearLayout>

 </layout>

対応するコミット

ここでのポイントは、TextViewのtext属性をviewModelのseeekBarValueStringプロパティに紐づけている箇所です。

activity_main.xml
  <TextView
 android:text="@{viewModel.seekBarValueString}"
 />

TextViewのtextを直接ViewModelのLiveData<String>型のプロパティに紐付けています。

MainActivity

MainActivityの編集に移ります。

まず、ViewModelのプロパティを追加します。

MainActivitity.kt
class MainActivity : AppCompatActivity() {
    private val viewModel by lazy {
        ViewModelProvider(this).get(MainViewModel::class.java)
    }
...

この際、ViewModelProviderのインポートを追加します。

対応するコミット

次に、バインディングオブジェクトのプロパティをMainActivityに追加します。自動生成されるこの型の名称は、対応するレイアウトファイルのファイル名をパスカルケースにし、 Bindingを末尾につけたものになります。

例:レイアウトファイル名がactivity_foo.xmlならActivityFooBindingになる

MainActivitity.kt
import example.android.android_data_binding_sample.databinding.ActivityMainBinding

class MainActivity : AppCompatActivity() {
...
    private lateinit var binding: ActivityMainBinding
...

対応するコミット

続いて、onCreate()内に、data bindingの設定を追記していきます。

プロジェクト生成時にデフォルトで書かれるsetContentView()をDataBindingUtilのそれに差し替え、バインディングクラスをインスタンス化します。

MainActivitity.kt
...
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
...

この際に、DataBindingUtilのインポート文も追加します。

対応するコミット

LiveDataオブジェクトをバインディングクラスとともに使用するため、MainActivityをライフサイクル所有者に指定します。

MainActivitity.kt
...
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.lifecycleOwner = this//追記
...

対応するコミット

続いて、先程定義したMainViewModelをバインディングクラスと共に使用するため、バインディングクラスのプロパティに指定します。

MainActivitity.kt
...
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        binding.lifecycleOwner = this
        binding.viewModel = viewModel//追記
...

対応するコミット

最後に、SeekBarの変化への対応を設定します。

MainActivitity.kt
...
    override fun onCreate(savedInstanceState: Bundle?) {
...
        binding.seekBar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
            override fun onProgressChanged(seekBar: SeekBar?, progress: Int, fromUser: Boolean) {
                this@MainActivity.viewModel.onSeekBarValueChanged(progress)
            }

            override fun onStartTrackingTouch(seekBar: SeekBar?) {
            }

            override fun onStopTrackingTouch(seekBar: SeekBar?) {
            }

        })
    }

対応するコミット

これで実行してみます。

シークバーを動かすと、TextViewの値が変わるのが確認できたでしょうか。

data_binding.gif

まとめ

ViewModelの(LiveDataの)プロパティをレイアウトファイル上のTextViewと紐付け、さらにSeekBarの変化をViewModelに伝える設定を行いました。

その結果、SeekBarの変化に応じてTextViewが変化するようになりました。

ユーザーがSeekBarを操作すると、次のようにメソッド呼び出し・プロパティの変化が生じます:

(MainActivity)
SeekBar.OnSeekBarChangeListener.onProgressChanged()の呼び出し

(MainViewModel)
viewModel.onSeekBarValueChanged()の呼び出し

_seekBarValue.postValue()の呼び出し

seekBarValueStringのvalueプロパティが更新される

(activity_main.xml)
seekBarValueStringに紐付けられたTextViewのtextプロパティが更新される

以上になります。

ここまでお読みいただきありがとうございます。お疲れ様でした。

参考

レイアウト ビューをアーキテクチャ コンポーネントにバインドする | developer.android.com

AndroidのデータバインディングとLiveDataを使ってみる | SONICMOOV LAB

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした