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.