I haven’t encountered a good article for a long time ---- [Aspect-oriented] Android handles repeated button clicks gracefully

Android handles repeated button clicks gracefully

 

 

Previous treatment

The methods found online or you might think of are probably these:

1. In each button click event, record the click time and judge whether it exceeds the click 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 to write a time judgment, and a lot of repeated code.

2. Encapsulate a click event to handle the click interval judgment

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 with the first method, this method encapsulates the judgment of repeated clicks inside the CustomClickListener, and there is no need to process time judgment externally, only the click method needs to be implemented.

3. Use RxAndroid to handle repeated clicks

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

Responsively handle button clicks and use rxjava operators to prevent repeated clicks. Compared with the first and second solutions, this method is more elegant.

think for a while:

These three methods, no matter which one, are very intrusive to the original click event. Either you need to add a method to the Click event, or you need to replace the entire Click event. Then, is there a way? Without changing the original logic, can it handle repeated clicks of the button well?

A more elegant approach

Add a unified processing logic to all methods of the same type, and we can quickly think of a word: AOP , yes, aspect-oriented programming.

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

1. Introduce Aspectj

Use AOP programming on Android, generally use the Aspectj library

Standing on the shoulders of giants, Hujiang has open sourced Aspectj's Gradle plugin, which is convenient for us to use Aspectj

  • In the build.gradle under the project root directory, add dependencies:
dependencies {
     ......
     classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.0'
}
  • In the build.gradle under the app or other module 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 for adding custom annotations is to facilitate the management of which methods use the repeated click AOP, and at the same time, you can pass in the click time interval in the annotation, which is more flexible.

3. Encapsulate a repeated click judgment tool class

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;
        }
    }
}

4. Write Aspect AOP processing class

@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 annotation is needed to prevent repeated clicks of the button. All other tasks are handed over to the compiler. The code is refreshed a lot.

 

 

 

--------------------------------------------

Introduction of AspectJ

The introduction of eclipse and Android Studio is different. This article only introduces how to introduce AspectJ into Android Studio. Please Baidu for eclipse. Android Studio needs to be introduced in the build.gradle file of the app module, which is divided into 3 steps:

1) Add core dependencies

dependencies {
    ...
    compile 'org.aspectj:aspectjrt:1.8.9'
}

2) Write gradle compilation script

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath 'org.aspectj:aspectjtools:1.8.9'
        classpath 'org.aspectj:aspectjweaver:1.8.9'
    }
}

Guess you like

Origin blog.csdn.net/zwx_lucky/article/details/102543646