Android data binding - cannot find getter for <> that accepts parameter type 'long'

Viewed 3518

I have been using data binding for the past few weeks and now am trying to use a two way data binding for a custom view with a 'value' attribute.

My problem is that I get the following error when building.

Cannot find a getter for <com.twisthenry8gmail.dragline.DraglineView app:value> that accepts parameter type 'long'

Now it was my understanding that the binding library will automatically use my public setters and getters however the most confusing part is adding a redundant inverse binding adapter seems to solve the problem? So I get the impression that it is using my setter without needing an adapter but this is not the case for the getter?

My custom view

class DraglineView(context: Context, attrs: AttributeSet) : View(context, attrs) {
    ...

    var value = 0L
        set(value) {

            draggedValue = value
            field = value
            invalidate()
        }

    ...
}

My view in the layout file

<com.twisthenry8gmail.dragline.DraglineView
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:increment="@{viewmodel.type.minIncrement}"
    app:minValue="@{viewmodel.type.minIncrement}"
    app:value="@={viewmodel.target}" />

My seemingly redundant adapter

@InverseBindingAdapter(attribute = "value")
@JvmStatic
fun getValueTest(draglineView: DraglineView): Long {

    return draglineView.value
}

My attribute changed adapter

@BindingAdapter("valueAttrChanged")
@JvmStatic
fun setDraglineListener(draglineView: DraglineView, listener: InverseBindingListener) {

    draglineView.valueChangedListener = {

        listener.onChange()
    }
}
2 Answers

The problem is that databinding system doesn't know when the view changes the value.

InverseBindingAdapter not only describes how to retrieve the value from the view, but it also defines an optional event property which will receive an InverseBindingListener instance. The default event name is the attribute name suffixed with "AttrChanged".

Now let's look at your setDraglineListener() adapter. It processes valueAttrChanged attribute added by InverseBindingAdapter and receives InverseBindingListener. The only thing is left is to notify the listener when the value is changed by calling listener.onChange();

I had a similar issue when using two-way data binding between my data object and a custom NumberPicker (error message at compile time: Cannot find a getter for <com.shawnlin.numberpicker.NumberPicker app:np_value> that accepts parameter type 'int' ).

The library I am using for the NumberPicker is https://github.com/ShawnLin013/NumberPicker and, according to its documentation, the current value should be set using np_value. It works correctly (the value gets initialized as required), except the two-way databinding which is giving me the error above.

However, changing app:np_value to android:value (in the annotations and in the layout) solved the issue.

In your case, you can try replacing

@InverseBindingAdapter(attribute = "value")
@BindingAdapter("valueAttrChanged")
app:value="@={viewmodel.target}

with

@InverseBindingAdapter(attribute = "android:value")
@BindingAdapter("android:valueAttrChanged")
android:value="@={viewmodel.target}"
Related