Data BindingでDelegates.observableを使う際に非nullの初期値を強制されたくない件
Data Binding Library で notifyPropertyChanged を利用する際、普通にやると下記のように非 null の初期値が強制されてしまう。
class MyModel : BaseObservable() { @get:Bindable var myText: String by Delegates.observable("") { _, _, _ -> notifyPropertyChanged(BR.myText) } }
しかし、lazy のように get 時には非 null だが初期値は null にしたいというケースがある。
でも、下記のようにできるとラクチンっすね。
class MyModel : BaseObservable() { @get:Bindable var myText: String by dataBindingObservable(BR.myText) }
ということで、以下のようなコードで対応する。
// 上記から dataBindingObservable(BR.myText) って呼ばれるやつ。 fun <T> BaseObservable.dataBindingObservable(brId: Int): ReadWriteProperty<Any?, T> = lazyObservable { _, _, _ -> notifyPropertyChanged(brId) } /** * @see kotlin.properties.Delegates.observable */ inline fun <T> lazyObservable(crossinline onChange: (property: KProperty<*>, oldValue: T?, newValue: T) -> Unit): ReadWriteProperty<Any?, T> = object : LazyObservableProperty<T>() { override fun afterChange(property: KProperty<*>, oldValue: T?, newValue: T) = onChange(property, oldValue, newValue) } /** * @see kotlin.properties.ObservableProperty */ abstract class LazyObservableProperty<T> : ReadWriteProperty<Any?, T> { private var value: T? = null protected open fun beforeChange(property: KProperty<*>, oldValue: T?, newValue: T): Boolean = true protected open fun afterChange(property: KProperty<*>, oldValue: T?, newValue: T) {} override fun getValue(thisRef: Any?, property: KProperty<*>): T { return value ?: throw IllegalStateException("Property ${property.name} should be initialized before get.") } override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) { val oldValue = this.value if (!beforeChange(property, oldValue, value)) { return } this.value = value afterChange(property, oldValue, value) } }
おまけ。
// notifyChange() を使いたい場合 fun <T> BaseObservable.dataBindingObservable(): ReadWriteProperty<Any?, T> = lazyObservable { _, _, _ -> notifyChange() } // 初期値と brId を指定したい場合 fun <T> BaseObservable.dataBindingObservable(initialValue: T, brId: Int): ReadWriteProperty<Any?, T> = Delegates.observable(initialValue) { _, _, _ -> notifyPropertyChanged(brId) } // 初期値を指定して notifyChange() を使いたい場合 fun <T> BaseObservable.dataBindingObservable(initialValue: T): ReadWriteProperty<Any?, T> = Delegates.observable(initialValue) { _, _, _ -> notifyChange() }