防止按钮重复点击
按钮的 OnClick
事件是 Android 开发中最常见的事件,比如 Button 常常和 OnClickListener
绑定在一起,当 OnClick
事件发生时,就会回掉 OnClickListener
的 onClick(View)
方法,比如
findViewById(R.id.button_id).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// ...
}
});
我们会在 onClick(View)
方法中做一些操作,比如在登陆按钮点击之后就会执行登陆操作,下订单按钮点击之后就会执行下单操作。
但是有个细节我们常常忽略,如果用户很短的时间内快速地对按钮点击多次,这样会造成 onClick(View)
方法被执行多次,就会造成重复登陆和重复下单的问题。这个问题有时候会造成一些错误的后果,在开发的过程中需要避免这样的问题发生。针对这种问题,有几种方法可以实现
标志位
比如一个登陆按钮,在 onClick(View)
方法体中,先判断标志位是否为 true
,如果为 true
说明业务逻辑正在执行,就直接 return
,否则就执行业务逻辑。当业务逻辑执行完毕之后再将标志位复位
private boolean mLoging;
private void initView() {
findViewById(R.id.button_id).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mLoading) {
return;
}
login();
}
});
}
private void login() {
ApiWrapper.login(account, password, new CallBack() {
@Override
public void onSuccess() {
// ...
mLoading = false;
}
@Override
public void onFailure() {
// ...
mLoading = false;
}
})
}
采用 标志位
这样的方法挺好的,就是需要额外维护一个标志位。一个按钮还好,如果按钮多了话,就需要维护多个标志位,有时候还容易忘了将标志位复位。
重写OnClickListener——标志位法
public abstract class NoClickQuicklyListener implements View.OnClickListener {
private boolean mClickable = true;
@Override
public void onClick(View v) {
if (mClickable) {
mClickable = false;
onClick();
}
}
public void reset() {
mClickable = true;
}
public abstract void onClick();
}
把标志位封装进 NoClickQuicklyListener
类中。不过感觉也不是很方便的样子,只是比第一种写法少了一点代码。
重写OnClickListener——时间间隔法
自定义一个类 NoClickQuicklyListener
实现 OnClickListener
接口。在类中维护一个记录时间的变量 lastClickTime
,如果这一次点击事件发生的时间和上一次点击事件发生的时间的间隔太短,少于 MIN_CLICK_OFFSET_TIME
定义的时间间隔,就不执行业务逻辑
public abstract class NoClickQuicklyListener implements View.OnClickListener {
private static final int MIN_CLICK_OFFSET_TIME = 1000;
private long lastClickTime = 0;
@Override
public void onClick(View v) {
long currentTimeMillis = System.currentTimeMillis();
if (currentTimeMillis - lastClickTime > MIN_CLICK_OFFSET_TIME) {
lastClickTime = currentTimeMillis;
onClick();
}
}
public abstract void onClick();
}
使用的时候,使用 NoClickQuicklyListener
代替 OnClickListener
findViewById(R.id.simple_button_id).setOnClickListener(new NoClickQuicklyListener() {
@Override
public void onClick() {
// ...
}
});
用这样的实现,就不需要维护额外的标志位了,代码少了很多,逻辑也会简单点。
使用RxJava的throttleFirst操作符
throttleFirst
操作符只允许在一定时间间隔类的第一个事件发出,对于其他事件则屏蔽。在到达时间间隔之后,再发出下一个事件。宝石图如下
可以使用 RxView
配合 throttleFirst
操作符来实现
RxView.clicks(button)
.throttleFirst(2000, TimeUnit.SECONDS) // 时间间隔设置为2秒
.subscribe(new Action1<Void>() {
@Override
public void call(Void aVoid) {
// ...
}
});