Data binding ライブラリ サンプル

作ったサンプル

github

画面

f:id:yossan2:20210606140244p:plain

Set up

Gradle

アプリ側のbuild.gradleに以下を記述

android {

buildFeatures {
        dataBinding true
    }
}

Layoutファイル

layoutタグをルートに変更する

activity_main.xml

<?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">
    <data>
        <variable
            name="resultHandler"
            type="io.github.yossan.databindingsample.MainActivity.ResultListener" />
    </data>
    <androidx.constraintlayout.widget.ConstraintLayout 
  
....

ソースファイル側

layoutファイルに、Data bindingする必要がある。

MainActivity.kt

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

         // activity_main.xmlレイアウトファイルの場合
    DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
}
  • 生成されるDataBindingクラスは、レイアウトファイル名に基づく

サンプル

View Reference

viewのidを通して直接アクセスすることができる

activity_main.xml

<!-- 参照元 -->
<EditText  android:id="@+id/nameEditText"

<!-- 参照 -->
<TextView android:text="@{nameEditText.text}" 
  • _はCamel Caseに変換される name_edit_textnameEditText

Event Handling

activity_main.xml

    <data>
        <variable
            name="resultHandler"
            type="io.github.yossan.databindingsample.MainActivity.ResultListener" />
    </data>    
  • resultHandler: 生成されるDataBindingクラスの属性となる

2つの方式でかける

  • Method Reference
  • Listener bindings

Method Reference

<Button
    android:onClick="@{resultHandler::onClick}"/>

Listener bindings

ラムダ式が使える

<Button
    android:onClick="@{(view) -> resultHandler.onClick(view)}"/>

MainActivity.kt

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
        // resultHandlerにリスナーをつなげる
        binding.resultHandler = ResultListener(binding)
    }

    class ResultListener(val binding: ActivityMainBinding) : View.OnClickListener {
        override fun onClick(v: View) {
            binding.resultTextView.text = when(binding.ageEditText.text.toString().toInt()) {
                in 0..20 -> "NG"
                in 21..130 ->"OK"
                else -> "It's not human."
            }
        }
    }
}

プログラム側からViewに反映させる

github

lifecycleOwner を設定する必要がある。

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        viewModel = ViewModelProvider(this, object : ViewModelProvider.NewInstanceFactory() {
            override fun <T : ViewModel?> create(modelClass: Class<T>): T {
                return MainViewModel() as T
            }
        }).get(MainViewModel::class.java)

        val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)

        // これがないとプログラミング側での変更がlayoutに反映されない
        binding.lifecycleOwner = this
        binding.viewModel = viewModel
    }

layout file

<?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"

    >
    <data>
        <import type="androidx.lifecycle.MutableLiveData" />
        <import type="io.github.yossan.drawsample.MainViewModel" />
        <import type="io.github.yossan.drawsample.DrawView" />
        <variable
            name="viewModel"
            type="MainViewModel" />
    </data>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:custom="http://schemas.android.com/apk/res/io.github.yossan.drawsample"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <io.github.yossan.drawsample.DrawView
        android:id="@+id/draw_view"
        android:layout_width="0dp"
        android:layout_height="0dp"
        custom:mode="@{viewModel.mode}"
        app:layout_constraintBottom_toTopOf="@+id/toolbar"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="1.0" />

    <androidx.appcompat.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?attr/colorPrimary"
        android:minHeight="?attr/actionBarSize"
        android:theme="?attr/actionBarTheme"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent">

        <Button
            android:id="@+id/pen"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:enabled="@{viewModel.mode == DrawView.Mode.pen ? false : true}"
            android:onClick="@{_ -> viewModel.toggleMode()}"
            android:text="@string/pen"
            />

        <Button
            android:id="@+id/eraser"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:enabled="@{viewModel.mode == DrawView.Mode.eraser ? false : true}"
            android:onClick="@{_ -> viewModel.toggleMode()}"
            android:text="@string/eraser" />
    </androidx.appcompat.widget.Toolbar>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>