Android 再入門 〜データバインディング②〜

前回

ボタンをクリックしたら名前が変わるようにしたい

User.kt の firstName を可変にする。

data class User(
        var firstName: String,
        val lastName: String
)

MainActivity.kt

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

        val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        val user = User("Taro", "Tanaka")
        binding.setUser(user)

        val button: Button = findViewById(R.id.button)
        button.setOnClickListener {
            Log.d("DEBUG", "Before:" + user.toString())
            user.firstName.set("Jiro")
            Log.d("DEBUG", "After:" + user.toString())
        }
    }

というふうに書いてもログには変更後の値が出力されるものの、 View の文字列を変更してくれなかった。

ここに書いてあるとおり、変更を通知するようにしなければならない。

build.gradle を修正

Kotlin でアノテーションを使用するので、 kotlin-kapt という Annotation processors をサポートしてくれるプラグインを追加する。

以前は他にも設定が必要だったが現状は必要ないそうだ。(参考)

build.gradle

apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'
// ▽追加
apply plugin: 'kotlin-kapt'

Data Class だと custom getter/setter を実装できないようなので、普通の Class にした。
(一応 別の変数名を使うことで擬似的にできるらしいがぱっと見わかりにくそう。 参考

User.kt

class User(
        firstName: String,
        val lastName: String
): BaseObservable() {
    @get:Bindable
    var firstName: String = firstName
        set(value) {
            field = value
            notifyPropertyChanged(BR.firstName)
        }
}

notifyPropertyChanged(BR.firstName) でデータバインディングで使用している firstName が変更されたよ!という通知を出すことで View に反映することができる。
BR クラスはコンパイル時に生成される。

別の方法

こっちの方法だと Data Class のままで OK。

User.kt

data class User(
        val firstName: ObservableField<String>,
        val lastName: String
)

MainActivity.kt

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

        val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
        // ObservableField クラスになったので修正
        val user = User(ObservableField("Taro"), "Tanaka")
        binding.setUser(user)

        val button: Button = findViewById(R.id.button)
        button.setOnClickListener {
            Log.d("DEBUG", "Before:" + user.toString())
            // set() で値をセットする
            user.firstName.set("Jiro")
            Log.d("DEBUG", "After:" + user.toString())
        }
    }

Failed to call observer method エラーが出る場合

android:text にセットする値は String に変換してあげる必要があった。

android:text="@{String.valueOf(user.age)}"