简体   繁体   English

Kotlin 带有扩展方法的数据绑定

[英]Kotlin databinding with extension methods

I'm trying to use Kotlin extension methods inside Android's databinding.我正在尝试在 Android 的数据绑定中使用 Kotlin 扩展方法。 For example;例如; calling an onclick handler.调用 onclick 处理程序。 So I've made this code:所以我做了这段代码:

posttest_list_item.xml 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 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 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.因此,当我将 clicked 方法放置在视图模型中时,它会按应有的方式完美运行。 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. e: [kapt] 发生异常:android.databinding.tool.util.LoggedErrorException:发现数据绑定错误。 cannot find method clicked(android.view.View) in class...PostItemViewModel在 class 中找不到方法 clicked(android.view.View)...PostItemViewModel

I've tried different things already, such as changing the android:onclick tag to PostTestItemViewModelExtensionKt instead of viewModel .我已经尝试过不同的方法,例如将android:onclick标记更改为PostTestItemViewModelExtensionKt而不是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.我正在使用 Kotlin 版本 1.2.71、gradle 3.2.0,并将databinding { enabled = true }kapt { generateStubs = true }添加到 my.gradle,并添加插件kotlin-androidkotlin-android-extensionskotlin-kapt定义。

Unfortunately you can't use extension methods as onClick callbacks. 不幸的是,你不能使用扩展方法作为onClick回调。

Extension methods in Kotlin are created as Java static methods while the Android framework is expecting an instance method. Kotlin中的扩展方法是作为Java静态方法创建的,而Android框架则是期望实例方法。

Note that in Android Studio you can decompile the Kotlin classes as Java to see the generated Java code. 请注意,在Android Studio中,您可以将Kotlin类反编译为Java,以查看生成的Java代码。

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.所以,今天(2022 年)我在我的一个项目中有相同的用例,我能够找到一种方法来使用数据绑定和自定义适配器为 android 视图实现自定义点击侦听器。

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我创建了一个名为 ViewExtensions.kt 的文件并添加了以下代码

 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 debouncing click 用于延迟给定时间的点击,在 xml 中调用如下所示的点击事件

 <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参考资料: https://proandroiddev.com/ensure-single-click-on-android-butterknife-did-it-right-48ef56153c78

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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