开发过程中,遇到了测试提出的bug,原因是做网络请求,生成一条数据的时候,网络响应慢,点击提交按钮没反应,多次点击,导致生成多条数据,数据出现问题。
出现问题,第一反应就是,一旦点击了网络请求按钮,就要设置它为不可点击,等请求回调,无论成功还是失败,再次设置为可点击。
部分代码:
requestNetData();
mBtLoading.setClickable(false);
@Override
public void onError(Call call, Exception e, int id) {
mBtLoading.setClickable(true);
Toaster.showToast(GetParkDataActivity.this, "数据保存失败");
return;
}
@Override
public void onResponse(String response, int id) {
mBtLoading.setClickable(true);
}
但是这样也会存在问题,我们都知道JAVA代码是按顺序执行的,如果当前手机网络不好,这个地方还是会出现多次点击事件,无法禁止快速的重复点击事件,
所以,有必要做一个点击时间间隔处理,和双击返回的原理一样。然后我写了以下代码:
public class ClickUtils {
private static long lastTime;
private static long curTime;
private static Toast mToast;
public static boolean mClick() {
if (System.currentTimeMillis() - lastTime >= 5000) {
curTime = System.currentTimeMillis();
lastTime = System.currentTimeMillis();
return true;
}
return false;
}
}
在这里我写了一个点击类,用的时候直接加判断就好了:
@OnClick(R.id.test)
public void onClick() {
if (ClickUtils.mClick()) {
// 下面是个吐司,我只是抽了一下 第一次点击到5s之后的点击响应这个事件
ClickUtils.mToaster(this, "111");
} else {
// 第一次点击之后 接下来的5s内 走这里
ClickUtils.mToaster(this, "222");
}
}
这样可以在业务代码里进行判断了,可以设置是否可点击,多少时间内可点击。
技术小白,开发探索中...如有什么不足,还请指正。
更新一下现在使用的防止连续点击类 增加RxJava
/**
* 类目名称 防止点击事件多次点击
*/
public class ClickFilterUtil {
// 防止连续点击的时间间隔
private static final long INTERVAL = 1000L;
// 上一次点击的时间
private static long lastClickTime;
private static long lastClickTimeRun;
public synchronized static boolean filter() {
long time = System.currentTimeMillis();
if (time - lastClickTime < INTERVAL) {
return true;
}
lastClickTime = time;
return false;
}
public synchronized static boolean filterRun() {
long time = System.currentTimeMillis();
if (time - lastClickTimeRun < INTERVAL) {
return true;
}
lastClickTimeRun = time;
return false;
}
/**
* 防止重复点击
*
* @param target 目标view
* @param listener 监听器
*/
@SuppressLint("CheckResult")
public static void preventRepeatedClick(final View target, long windowDuration, final View.OnClickListener listener) {
RxView.clicks(target).throttleFirst(windowDuration, TimeUnit.SECONDS).subscribe(new Observer<Object>() {
@Override
public void onError(Throwable e) {
}
@Override
public void onComplete() {
}
@Override
public void onSubscribe(Disposable d) {
}
@Override
public void onNext(Object object) {
listener.onClick(target);
}
});
}
/**
* 防止重复点击
*
* @param target 目标view
* @param listener 监听器
*/
@SuppressLint("CheckResult")
public static void preventRepeatedClick(final View target, final View.OnClickListener listener) {
preventRepeatedClick(target, 1, listener);
}
}
更新一些内容
上面的写法都是控制单个的点击,那样全局处理起来就很麻烦,每个点击都要加方法?那不就哭了,后面想到是不是可以从屏幕的触摸角度考虑,然后就有了下面的代码
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (isPreventFastClick()) {
if (ev.getAction() == MotionEvent.ACTION_DOWN){
// 判断连续点击事件时间差
if (SystemUIUtils.isFastClick()){
return true;
}
}
}
return super.dispatchTouchEvent(ev);
}
/**
* 是否连续点击
*/
static long lastClickTime = 0;
public static boolean isFastClick() {
boolean flag = true;
long currentClickTime = System.currentTimeMillis();
if ((currentClickTime - lastClickTime) >= 300) {
flag = false;
}
lastClickTime = currentClickTime;
return flag;
}
这里的 300 间隔 是根据使用场景测试总结的,自己可以去权衡一下。