Search code examples
androidkotlinobservableandroid-databinding

Android data binding with Kotlin, BaseObservable, and a custom delegate


I'm attempting to write a custom delegate which would clean up the syntax for databinding in a Kotlin class. It would eliminate the need to define a custom getter and setter for every property I might want to observe.

The standard implementation in Kotlin appears to be as follows:

class Foo : BaseObservable() {

    var bar: String
         @Bindable get() = bar
         set(value) {
             bar = value
             notifyPropertyChanged(BR.bar)
         }
}

Clearly, with a lot of properties this class can become pretty verbose. What I would like instead is to abstract that away into a delegate like so:

class BaseObservableDelegate(val id: Int, private val observable: BaseObservable) {

     @Bindable
     operator fun getValue(thisRef: Any, property: KProperty<*>): Any {
         return thisRef
     }

     operator fun setValue(thisRef: Any, property: KProperty<*>, value: Any) {
         observable.notifyPropertyChanged(id)
     }
}

Then, the class which extends BaseObservable could go back to having one-line variable declarations:

class Foo : BaseObservable() {
      var bar by BaseObservableDelegate(BR.bar, this)
}

The problem is that without the @Bindable annotation in the Foo class, no propertyId is generated in BR for bar. I'm unaware of any other annotation or method for generating that property id.

Any guidance would be appreciated.


Solution

  • You can annotate the default getter or setter without providing a body.

    var bar: String by Delegates.observable("") { prop, old, new ->
        notifyPropertyChanged(BR.bar)
    }
        @Bindable get
    

    There is a shortcut annotation use-site target which does the same thing.

    @get:Bindable var bar: String by Delegates.observable("") { prop, old, new ->
        notifyPropertyChanged(BR.bar)
    }