简体   繁体   中英

Kotlin databinding with extension methods

I'm trying to use Kotlin extension methods inside Android's databinding. For example; calling an onclick handler. So I've made this code:

posttest_list_item.xml

<?xml version="1.0" encoding="utf-8"?>

<data>
    <import type="android.view.View"/>
    <import type="com.example.test.post.posttest.PostTestItemViewModelExtensionKt" />
    <variable
        name="viewModel"
        type="com.example.test.post.posttest.PostTestItemViewModel" />
</data>
<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:paddingLeft="16dp"
    android:paddingRight="16dp"
    android:clickable="true"
    android:onClick="@{(view) -> viewModel.clicked(view)}"
    >
[...]

PostTestItemViewModel.kt

open class PostTestItemViewModel : ViewModel() {
    val postTitle = MutableLiveData<String>()
    val postBody = MutableLiveData<String>()

   /**
    * Binds the required properties/entities to this ViewModel
    */
   fun bind(post: Post) {
       postTitle.value = post.title
       postBody.value = post.body
   }
}

PostTestItemViewModelExtension.kt

fun PostTestItemViewModel.clicked(v: View) {
    this.postTitle.value = "clicked"
}

So when I place the clicked method inside the viewmodel, it works perfectly the way it should be. However, when I create it as an extension method, I get the following error on compilation:

e: [kapt] An exception occurred: android.databinding.tool.util.LoggedErrorException: Found data binding errors. cannot find method clicked(android.view.View) in class...PostItemViewModel

I've tried different things already, such as changing the android:onclick tag to PostTestItemViewModelExtensionKt instead of viewModel . Unfortunately all the things don't seem to work. So it looks like the extension method is getting generated after the databinding takes place. Is there a way around this or am I still doing something wrong? Or is it just not possible to bind extension methods?

I'm using Kotlin version 1.2.71, gradle 3.2.0 and have the databinding { enabled = true } and kapt { generateStubs = true } added to my.gradle, and have the plugings kotlin-android , kotlin-android-extensions and kotlin-kapt defined.

Unfortunately you can't use extension methods as onClick callbacks.

Extension methods in Kotlin are created as Java static methods while the Android framework is expecting an instance method.

Note that in Android Studio you can decompile the Kotlin classes as Java to see the generated Java code.

So, today(2022) I had the same use case in one of my projects and i was able to figure out a way to implement custom click listeners for android views using data binding and custom adapters.

The use case is:

Click event should not be triggered twice or to prevent accidental clicks from the user

I created a file called ViewExtensions.kt and added the following code

 class DebouncingOnClickListener(
      private val intervalMillis: Long,
      private val doClick: (() -> Unit)
    ) : View.OnClickListener {

    override fun onClick(v: View) {
        if (enabled) {
            enabled = false
            v.postDelayed(ENABLE_AGAIN, intervalMillis)
            doClick()
        }
    }

    companion object {
        @JvmStatic
        var enabled = true
        private val ENABLE_AGAIN =
            Runnable { enabled = true }
    }
}

@BindingAdapter("singleClick")
fun View.setSingleClick(doClick: () -> Unit) =
    setOnClickListener(
        DebouncingOnClickListener( 
            intervalMillis = 5000, //5ms delay for click event
            doClick = doClick
        )
    )

The debouncing click is used to defer the click for the given time, and in the xml called the click event like below

 <androidx.appcompat.widget.AppCompatButton
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Click Me"
        app:singleClick="@{()->fragment.clicked()}" />

Now I'm able to listen for click events on both fragment and in the viewmodel and the click is deferred for the given amount of time.

Hence the user cannot click the view accidentally multiple times.

References: https://proandroiddev.com/ensure-single-click-on-android-butterknife-did-it-right-48ef56153c78

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM