Yes, this problem occurs because it postpones click detection using post
and postDelayed
, and this will queue the ACTION_UP
events. This can be seen by the source code of the View
class:
// Use a Runnable and post this rather than calling
// performClick directly. This lets other visual state
// of the view update before click actions start.
if (mPerformClick == null) {
mPerformClick = new PerformClick();
}
if (!post(mPerformClick)) {
performClick();
}
I can not think of a better way to do it, the flag is enough.
But I think you can improve this a bit by making this logic centered on a subclass of Button
. This way you avoid code replication, and make it transparent to anyone who uses it. It would still be necessary to set setEnabled
because of asynchronous processing, otherwise just make the management of the enabled
flag before and after the performClick
call of the parent ( super
).
My suggestion would be:
public class SingleClickButton extends Button {
public SingleClickButton(Context context) {
super(context);
}
public SingleClickButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SingleClickButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public SingleClickButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public boolean performClick() {
return isEnabled() && super.performClick();
}
}
This solution will still queue events, but will not let it run over and over again. To avoid the accumulation of events, there would have to be onTouchEvent
overwriting, but it is a more aggressive approach and I think it is unnecessary.
To use in your layout, simply declare the tag with the entire path of the package and class:
<nome.do.seu.pacote.SingleClickButton
...
/>