The main interaction method for Android apps on mobile phones is clicking. After the user clicks, the App may do operations such as updating the UI within the page, opening a new page, or initiating a network request. The Android system itself does not process repeated clicks. If the user clicks multiple times in a short period of time, problems such as opening multiple pages or repeatedly initiating network requests may occur. Therefore, it is necessary to add codes to deal with repeated clicks that affect the repeated clicks.
Previous handling
Previously, I used RxJava's solution in the project, and used the third-party library RxBinding to prevent repeated clicks:
fun (interval: Long = 1000L, listener: (View) -> Unit) { (this) .throttleFirst(interval, ) .subscribe({ (this) }, { (it) }) }
But there is a problem with this. For example, if you use two fingers to click two different buttons at the same time, the button functions are all newly opened pages, so two new pages may be opened. Because Rxjava is implemented to prevent repeated clicks for a single control, not multiple controls.
Now how to deal with it
Now we use time judgment, which only responds to clicks once within the time range. By saving the last click time to the decorView in the Activity Window, all Views in an Activity share one last click time.
fun ( interval: Int = , isShareSingleClick: Boolean = true, listener: (View) -> Unit ) { setOnClickListener { val target = if (isShareSingleClick) getActivity(this)?.window?.decorView ?: this else this val millis = (.single_click_tag_last_single_click_millis) as? Long ?: 0 if (() - millis >= interval) { ( .single_click_tag_last_single_click_millis, () ) (this) } } } private fun getActivity(view: View): Activity? { var context = while (context is ContextWrapper) { if (context is Activity) { return context } context = } return null }
The default value of the parameter isShareSingleClick is true, which means that the control and other controls in the same Activity share the last click time. It can also be changed to false manually, which means that the control itself has a last click time.
mBinding. { // Process a single click} mBinding.(interval = 2000, isShareSingleClick = false) { // Process a single click}
Repeated clicks for other scenarios
Indirect setting click
In addition to click monitoring directly set on the View, other indirectly setting clicks also have scenes where repeated clicks need to be handled, such as rich text and lists.
To do this, the code that determines whether a single click is triggered is extracted, as a separate method:
fun ( interval: Int = , isShareSingleClick: Boolean = true, listener: (View) -> Unit ) { setOnClickListener { determineTriggerSingleClick(interval, isShareSingleClick, listener) } } fun ( interval: Int = , isShareSingleClick: Boolean = true, listener: (View) -> Unit ) { ... }
Call determineTriggerSingleClick directly in the click-listening callback to determine whether a single click is triggered. Let’s take rich text and list as examples.
Rich text
Inherit ClickableSpan and determine whether a single click is triggered in the onClick callback:
inline fun ( listener: (View) -> Unit, isShareSingleClick: Boolean = true, ... ): SpannableStringBuilder = inSpans( object : ClickableSpan() { override fun onClick(widget: View) { (interval, isShareSingleClick, listener) } ... }, builderAction = builderAction )
This will have a problem. The widget in the onClick callback is to set the control for rich text. That is to say, if there are multiple single clicks in the rich text, even if the value isShareSingleClick is false, these single clicks will still share the last click time of the setting rich text control.
Therefore, special processing is required here. When isShareSingleClick is false, create a fake View to trigger the click event, so that multiple single clicks in rich text with multiple clicks isShareSingleClick is false have their own fake View to exclusively enjoy the last click time.
class SingleClickableSpan( ... ) : ClickableSpan() { private var mFakeView: View? = null override fun onClick(widget: View) { if (isShareSingleClick) { widget } else { if (mFakeView == null) { mFakeView = View() } mFakeView!! }.determineTriggerSingleClick(interval, isShareSingleClick, listener) } ... }
Where rich text is set, use the settings onSingleClick to achieve a single click:
= () = = buildSpannedString { append("normalText") onSingleClick({ // Process a single click }) { color() { append("clickText") } } }
List
The list uses the RecyclerView control, and the adapter uses a third-party libraryBaseRecyclerViewAdapterHelper。
Item Click:
{ _, view, _ -> { // Process a single click } }
Item Child Click:
(.btn1, .btn2) { _, view, _ -> when () { .btn1 -> { // Handle ordinary clicks } .btn2 -> { // Process a single click } } }
Data binding
When using DataBinding, sometimes click events are set directly in the layout file, so the @BindingAdapte annotation is added to set a single click event in the layout file and make adjustments to the code. At this time, you need to replace listener: (View) -> Unit in the project with listener: .
@BindingAdapter( *["singleClickInterval", "isShareSingleClick", "onSingleClick"], requireAll = false ) fun ( interval: Int? = , isShareSingleClick: Boolean? = true, listener: ? = null ) { if (listener == null) { return } setOnClickListener { determineTriggerSingleClick( interval ?: , isShareSingleClick ?: true, listener ) } }
Set a single click in the layout file:
< android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/btn" app:isShareSingleClick="@{false}" app:onSingleClick="@{()->()}" app:singleClickInterval="@{2000}" />
Process a single click in the code:
class YourViewModel : ViewModel() { fun handleClick() { // Process a single click } }
Summarize
For the place where clicks are set directly on the View, if you need to deal with repeated clicks, use onSingleClick, and if you do not need to deal with repeated clicks, use the original setOnClickListener.
For indirectly setting clicks, if repeated clicks need to be processed, use determineTriggerSingleClick to determine whether a single click is triggered.
Project gallery
single-click, I think it’s very good to use, please don’t be stingy with your Star!
The above is the detailed content of how Android handles repeated clicks elegantly. For more information about Android processing repeated clicks, please follow my other related articles!