Search code examples
androidkotlinandroid-databinding

Typed parameter in a lambda not recognized by the data binder when using BindingAdapter


I am trying to bind the following elements:

xml:

<android.support.v7.widget.SwitchCompat
    ...
    bind:onCheckedChanged="@{(isChecked) -> viewModel.onCheckedChanged(isChecked)}"
    .../>

viewModel:

class MyViewModel() {
    fun onCheckedChanged(isChecked: Boolean) {
        ...
    }
}

using a BindingAdapter:

@BindingAdapter("onCheckedChanged")
fun bindOnCheckedChanged(view: SwitchCompat, onCheckedChanged: (Boolean) -> Unit) {
    view.setOnCheckedChangeListener(
        { _, isChecked ->
            if (view.isPressed) onCheckedChanged(isChecked)
        }
    )
}

The error I get is this one:

data binding error ****msg:cannot find method onCheckedChanged(java.lang.Object) in class MyViewModel

It seems that the data binder does not recognize isChecked as a Boolean. I tried to force the typing in the xml like isChecked:Boolean but I get a bunch of different errors.

Right now I made it work by using Any instead of Boolean but I feel like it's wrong:

@BindingAdapter("onCheckedChanged")
fun bindOnCheckedChanged(view: SwitchCompat, onCheckedChanged: (Any) -> Unit) {
    ...

and

fun onCheckedChanged(isChecked: Any) {
    val isSwitchChecked = isChecked as? Boolean ?: return
    ...

Does anyone know how to make it work the correct function signature?


Solution

  • According to Binding Adapters documentation: "Event handlers may only be used with interfaces or abstract classes with one abstract method, as shown in the following example:

    @BindingAdapter("android:onLayoutChange")
    fun setOnLayoutChangeListener(
        view: View,
        oldValue: View.OnLayoutChangeListener?,
        newValue: View.OnLayoutChangeListener?
    ) {
      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
          if (oldValue != null) {
              view.removeOnLayoutChangeListener(oldValue)
          }
          if (newValue != null) {
              view.addOnLayoutChangeListener(newValue)
          }
      }
    }
    

    " DB docs never specify you can use Kotlin lambdas in Binding Adapters In this case you need to create an interface with only one method like this:

    interface CustomOnCheckedListener {
        fun onChecked(isChecked: Boolean)
    } 
    

    then your Binding Adapter:

    @BindingAdapter("onCheckedChanged")
    fun bindOnCheckedChanged(view: SwitchCompat, onCheckedChanged: CustomCheckListener) {
        view.setOnCheckedChangeListener(
            { _, isChecked ->
                if (view.isPressed) onCheckedChanged.onChecked(isChecked)
            }
        )
    }
    

    in your ViewModel class you need to create a function with the same signature of the listener's method:

    fun onChecked(isChecked: Boolean){
        //some code here
    }
    

    and in your xml simply pass a reference to your function:

    "@{viewModel::onChecked}"