Reputation: 121
i want to set a certain action (like preventing multiple click) on every click event in data binding , in other phrase when a user click on each view, first do a specific action and after that do action relevant to clicked view(different for each view). How can I do this? description: i implement MVVM and use databinding
Upvotes: 1
Views: 3588
Reputation: 137
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
Upvotes: 0
Reputation: 370
First: create a mutableLiveData of type boolean in your SomeViewModel class with initial value to true
val data = MutableLiveData<Boolean>(true)
next in your xml
<data>
<variable
name="viewModel"
type="..SomeViewModel" />
</data>
<View
android:enabled = "@{viewModel.data}" // if working with button
android:clickable = "@{viewModel.data}" // for views which dont have enable tag
android:onClick="@{() -> viewModel.disableButtonAndPerformRequiredAction()}"/>
// In viewmodel
fun disableButtonAndPerformRequiredAction() {
data.value = false // it will disable the click for the view
// Perform other tasks
// post executing required task set
data.value = true // it will again enable the click for the view
}
Upvotes: 2
Reputation: 2132
This is what I do in this situation.
First: add onclick in your xml that call method on view model and pass it view
XML:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
<variable
name="model"
type="....ViewModel" />
</data>
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:onClick="@{(v)-> model.onClick(v)}"/>
</layout>
Second: adding prevent double click with kotlin extensions Kotlin:
fun View.preventDoubleClick() {
isClickable = false
Handler().postDelayed({ isClickable = true },500L)
}
Third:
Kotlin:
fun onClick(view: View?){
view?.preventDoubleClick()
}
now you have access to your view that clicked in view model. remember make your view nullable. this help you when for example you want add unit test for your method you can just send view null.
Upvotes: 2