Issue with higher order function as a binding adap

2019-03-18 06:19发布

问题:

I am running into issues trying to take a function as a parameter in a binding adapter using Kotlin/Android databinding. This example code throws e: error: cannot generate view binders java.lang.StackOverflowError when building with no other useful info in the log.

Here is my binding adapter:

@JvmStatic
@BindingAdapter("onDelayedClick")
fun onDelayedClick(view: View, function: () -> Unit) {
    // TODO: Do something
}

XML:

        <View
            android:id="@+id/test_view"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:onDelayedClick="@{() -> viewModel.testFunction()}"/>

and method in my ViewModel:

fun testFunction() = Unit

I have been struggling with this for a bit now and nothing I've tried works, so any help is appreciated.

回答1:

Use function: Runnable instead of function: () -> Unit.

Android's data-binding compiler generates java code, to which, your kotlin function's signature looks like void testFunction(), as kotlin adapts Unit as void when calling from java.

On the other hand, () -> Unit looks like kotlin.jvm.functions.Function0 which is a function which takes 0 inputs and returns Unit.INSTANCE.

As you can see these two function signatures don't match, and that's why the compilation fails.



回答2:

In the Event Handling section I came across this line:

In Listener Bindings, only your return value must match the expected return value of the listener (unless it is expecting void)

For more about error :

cannot generate view binders java.lang.StackOverflowError

read this article. hope it will help you!!



回答3:

The declaration () -> Unit suggests a function which takes no input and returns nothing (Unit is the return type in this statement). Your function should look like this:

fun testFunction() = {}


回答4:

Put apply plugin: 'kotlin-kapt' in build.gradle

Then you can create Binding Adapter like

@JvmStatic
@BindingAdapter("onDelayedClick")
fun onDelayedClick(view: View, function: () -> Unit) {
    // TODO: Do something
}

And XML Like

<View
   android:id="@+id/test_view"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   app:onDelayedClick="@{viewModel.testFunction}"/>

And VM Like

val testFunction =  {
    // TODO: Do something
}