Android gracefully handle button repeatedly clicking

App, a large part of the scene is to click the button to submit the data to the server, because the network request will take time, users are likely to click multiple times, resulting in duplication of data submission, causing a variety of strange problems.
Therefore, to prevent multiple clicks a button, Android development is a very important technical means.

Reproduced in: https://www.jianshu.com/p/7b354eb8d0d3

Previous treatment

To the Internet to find, or the way you might think about these:

1. Each button click event, the recording time of the click, click over to determine whether time interval

private long mLastClickTime = 0;
public static final long TIME_INTERVAL = 1000L;
private Button btTest;
private void initView() {
    btTest = findViewById(R.id.bt_test);
    btTest.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            long nowTime = System.currentTimeMillis();
            if (nowTime - mLastClickTime > TIME_INTERVAL) {
                // do something
                mLastClickTime = nowTime;
            } else {
                Toast.makeText(MainActivity.this, "不要重复点击", Toast.LENGTH_SHORT).show();
            }
        }
    });
}

In this way, each click event needs time to write a judgment, a lot of duplicate code.

2. Package a click event, click interval judgment processing

public abstract class CustomClickListener implements View.OnClickListener {
    private long mLastClickTime;
    private long timeInterval = 1000L;

    public CustomClickListener() {

    }

    public CustomClickListener(long interval) {
        this.timeInterval = interval;
    }

    @Override
    public void onClick(View v) {
        long nowTime = System.currentTimeMillis();
        if (nowTime - mLastClickTime > timeInterval) {
            // 单次点击事件
            onSingleClick();
            mLastClickTime = nowTime;
        } else {
            // 快速点击事件
            onFastClick();
        }
    }

    protected abstract void onSingleClick();
    protected abstract void onFastClick();
}

use:

btTest.setOnClickListener(new CustomClickListener() {
    @Override
    protected void onSingleClick() {
        Log.d("xxx", "onSingleClick");
    }

    @Override
    protected void onFastClick() {
        Log.d("xxx", "onFastClick");
    }
});

Compared to the first embodiment, this method of determination is repeated clicks encapsulated within CustomClickListener, without external processing time determination, the method can only implement one click.

3. Use RxAndroid process repeated clicks

RxView.clicks(view)
    .throttleFirst(1, TimeUnit.SECONDS)
    .subscribe(new Consumer<Object>() {
        @Override
        public void accept(Object o) throws Exception {
            // do something
        }
     });

Responsively processing button clicks, using rxjava operator, to prevent duplicate clicks, compared to the first and second embodiment, the method is more grace.

think for a while:

These three methods, no matter what kind, are there a lot of the original invasive click event, or you need to add the Click event method, or you need to replace the entire Click event, then, is there a way you can without changing the original logic, but also a good deal of repeat button click it?

More elegant approach

To all methods of the same type are combined with a unified processing logic, we will soon be able to think of a word: AOP , yes, Aspect Oriented Programming.

How to use AOP to solve the problem of repeated clicks?

1. The introduction of Aspectj

Android uses AOP programming on the general use of this library Aspectj

Standing on the shoulders of giants, Shanghai River has open sourced Aspectj of Gradle plugins to help us use Aspectj

  • In build.gradle in the project root directory, add dependencies:
dependencies {
     ......
     classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.0'
}
  • In build.gradle or other module in the app directory, add:
// 注意:主App中请确保添加aspectjx
apply plugin: 'android-aspectjx'
dependencies {
    ......
    implementation 'org.aspectj:aspectjrt:1.8.9'
}

2. Add a custom annotation

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SingleClick {
    /* 点击间隔时间 */
    long value() default 1000;
}

The reason is to add custom annotations, which uses AOP method to facilitate the management of repeated clicks, and clicks can pass the time interval in the annotation, more flexible.

3. Package Tools determining a repetitive clicks

public final class XClickUtil {

    /**
     * 最近一次点击的时间
     */
    private static long mLastClickTime;
    /**
     * 最近一次点击的控件ID
     */
    private static int mLastClickViewId;

    /**
     * 是否是快速点击
     *
     * @param v  点击的控件
     * @param intervalMillis  时间间期(毫秒)
     * @return  true:是,false:不是
     */
    public static boolean isFastDoubleClick(View v, long intervalMillis) {
        int viewId = v.getId();
        long time = System.currentTimeMillis();
        long timeInterval = Math.abs(time - mLastClickTime);
        if (timeInterval < intervalMillis && viewId == mLastClickViewId) {
            return true;
        } else {
            mLastClickTime = time;
            mLastClickViewId = viewId;
            return false;
        }
    }
}

Aspect AOP treatment class 4. Preparation of

@Aspect
public class SingleClickAspect {
    private static final long DEFAULT_TIME_INTERVAL = 5000;

    /** 
     * 定义切点,标记切点为所有被@SingleClick注解的方法
     * 注意:这里me.baron.test.annotation.SingleClick需要替换成
     * 你自己项目中SingleClick这个类的全路径哦
     */
    @Pointcut("execution(@me.baron.test.annotation.SingleClick * *(..))")
    public void methodAnnotated() {}

    /** 
     * 定义一个切面方法,包裹切点方法
     */
    @Around("methodAnnotated()")
    public void aroundJoinPoint(ProceedingJoinPoint joinPoint) throws Throwable {
        // 取出方法的参数
        View view = null;
        for (Object arg : joinPoint.getArgs()) {
            if (arg instanceof View) {
                view = (View) arg;
                break;
            }
        }
        if (view == null) {
            return;
        }
        // 取出方法的注解
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        Method method = methodSignature.getMethod();
        if (!method.isAnnotationPresent(SingleClick.class)) {
            return;
        }
        SingleClick singleClick = method.getAnnotation(SingleClick.class);
        // 判断是否快速点击
        if (!XClickUtil.isFastDoubleClick(view, singleClick.value())) {
            // 不是快速点击,执行原方法
            joinPoint.proceed();
        }
    }
}

Instructions

private void initView() {
    btTest = findViewById(R.id.bt_test);
    btTest.setOnClickListener(new View.OnClickListener() {
        // 如果需要自定义点击时间间隔,自行传入毫秒值即可
        // @SingleClick(2000)
        @SingleClick
        @Override
        public void onClick(View v) {
            // do something
        }
    });
}

Only one comment, to complete the button to prevent duplicate clicks, all the other work to the compiler, the code has a lot of fresh wood.



Author: XBaron
link: https: //www.jianshu.com/p/7b354eb8d0d3
Source: Jane book
Jane book copyright reserved by the authors, are reproduced in any form, please contact the author to obtain authorization and indicate the source.

Guess you like

Origin blog.csdn.net/taoerchun/article/details/91533628