一行代碼解決安卓重複點擊

拋出問題

「大哥,有個問題想問你!」java

「哎,說吧(心裏戲:咋又來了。。。準沒好事!)」android

「個人一個頁面中有一個查詢按鈕,點擊就會發出網絡請求,等待返回結果後更新數據。」git

「這不挺好的嘛!有啥問題啊?」github

「對,我也以爲沒問題,但測試不按套路出牌啊,測試那邊的網絡不太好,她點擊按鈕以後因爲網絡比較慢就快速多點擊了幾下,而後。。。」編程

「而後怎麼了?ANR了吧?」bash

「你咋知道的大哥?」網絡

「來吧,幫您看看吧!」app

平常開發中確定遇到過這種狀況,接下來我們就來看看該怎麼解決這種問題。maven

第一種:彈窗等待

「小子,過來,你看啊,你能夠這樣,當你點擊了按鈕以後就彈出一個對話框,設置成不能關閉,等網絡請求完成以後再將對話框關閉了就行」ide

「這是一種方式,但我該怎麼寫呢?」

「哎,給你寫一下例子吧:照着下面代碼寫就行」

public void btnDialog(View view) {
        ProgressDialog progressDialog = new ProgressDialog(this);
        progressDialog.setTitle("等待");
        progressDialog.setMessage("等待內容");
        //progressDialog.setCanceledOnTouchOutside(false);
        progressDialog.show();
    }
複製代碼

上面代碼很簡單,這只是一種思路,點擊按鈕後能夠彈出對話框不讓用戶進行操做(註釋的那一行代碼就是禁止用戶點擊的),當請求完成以後再將對話框關閉。

不過不推薦這種作法,官方也不推薦,官方推薦使用ProgressBar。

第二種:禁止點擊

「大哥,我以爲彈出對話框不太好,會讓用戶很反感,還有別的方式嗎?」

「行了,早就準備好和你說了,還不止一種呢!這種方式更簡單了,只須要設置按鈕是否能夠點擊就行,當用戶點擊後設置按鈕不可點擊,請求完成以後再設置能夠點擊就好了,這個不用我寫代碼了吧?「

」嘿嘿,這個不用,你剛纔說還有好幾種,說來聽聽啊!「

第三種:時間判斷

這種方式比上面的稍微麻煩點,但仍是很簡單。

具體操做就是定義兩個變量,一個爲上次點擊時間,一個爲點擊的間隔時間。

// 上次點擊時間
private long lastClickTime = 0L;
// 兩次點擊間隔時間(毫秒)
private static final int FAST_CLICK_DELAY_TIME = 1500;
複製代碼

在點擊時進行判斷就能夠了,來看一下代碼吧:

public void btnInter(View view) {
  if (System.currentTimeMillis() - lastClickTime >= FAST_CLICK_DELAY_TIME) 
     lastClickTime = System.currentTimeMillis();
  }
}
複製代碼

"大哥,這種方法看着比上面兩種好用多了,只須要這樣定義一下就好了,我就用這一種啊!"

「先別高興的太早了!」

第四種:AOP實現

「大哥,你剛纔說我高興的太早了是爲啥啊?「

」你只有一個頁面的話這樣寫確定是沒有問題的,可是若是有多個頁面都有防止按鈕重複點擊的需求呢?每一個頁面都定義一遍啊?「

」呃呃,你說的對,大哥,那應該怎麼辦呢?「

」你知道AOP嗎?接下來我要說的就和它有關「

」AOP?那是什麼鬼?我知道OOP!「

」不錯啊小子,還知道OOP,OOP就是面向對象,而AOP就是面向過程。「

AOP爲Aspect OrientedProgramming的縮寫,意爲面向切面編程。所謂的面向切面編程實際上是對業務邏輯又進行了進一步的抽取,將多種業務邏輯中的公用部分抽取出來作成一種服務(好比日誌記錄,性能統計等),從而實現代碼複用。另外這種服務經過配置能夠動態的給程序添加統一控制,利用AOP能夠對業務邏輯的各個部分進行分離,從而使得業務邏輯各部分之間的耦合度下降。

AOP並非Android中的產物,而是Java中的,Android官方並無提供,因此想使用AOP首先要導入能夠實現AOP的三方庫:

在項目級別的build.gradle中新增如下代碼:

classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4'
複製代碼

而後在moudle的build.gradle中新增依賴:

implementation 'org.aspectj:aspectjrt:1.8.14'
複製代碼

還須要在moudle的build.gradle中最上面添加如下代碼:

apply plugin: 'android-aspectjx'
複製代碼

完事以後點擊sync Now更新一下。

由於想在全部地方都能使用,因此來定義一個註解:

/** * 實現防止按鈕連續點擊 * @author jiang zhu on 2020/4/19 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface SingleClick {
    /* 點擊間隔時間 */
    long value() default 1500;
}
複製代碼

註解你們應該都使用過,運行時就不說了,使用範圍定義的是方法上,有一個參數爲間隔時間,這個參數意思其實和第三種方案中的相似。

而後就須要定義一個類,裏面實現AOP的具體操做:

@Aspect
public class SingleClickAspect {}
複製代碼

而後定義切點,標記切點爲全部被@SingleClick的方法:

@Pointcut("execution(@com.zj.singclick.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 (!isFastDoubleClick(view, singleClick.value())) {

            joinPoint.proceed();
        }
    }
複製代碼

來簡單分析下,首先看註解中定義了剛纔定義的切點方法,而後取出方法的參數,再取出方法的註解,而後調用

isFastDoubleClick方法判斷是否爲快速點擊事件,若是是什麼都不幹,若是不是則執行原方法中的內容。

下面貼一下isFastDoubleClick方法的代碼:

private static long mLastClickTime;
    private static int mLastClickViewId;

    private 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;
        }
    }
複製代碼

上面代碼採用了View的id和間隔時間雙重判斷,即便一個頁面的多個按鈕均可區分。

「大哥,停停停,都給我說懵了,這寫完這個該怎麼用啊!」

「彆着急,下面就教你該怎麼使用!」

使用很簡單,只須要在點擊時間方法上加上我們自定義的註解就好了,還能夠設置間隔時間,若是不寫的話就是默認值,上面也寫了,默認值就是1500毫秒。

@SingleClick(2000)
    public void btnAop(View view) {
        ToastUtils.showShort("btnAop");
        Log.e(TAG, "btnAop");
    }
複製代碼

「是否是很簡單啊?」

「大哥,我也不想寫這一大堆,我只想用,你能封裝成一個庫嗎?我用的時候直接調用就行!」

「哎,行吧,我封裝一下。。。。」

第四種封裝

「我已經將上面第四種方式封裝爲了一個庫並上傳到了JitPack庫,下面給你說一下使用方法吧!」

使用方法很簡單,須要幾步配置,配置完成以後直接添加註解便可使用,下面是配置方法: 一、在項目的build.gradle中的buildscript中的dependencies添加:

dependencies {
        ...
        classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4'
 }
複製代碼

二、在項目的build.gradle中的allprojects中的repositories添加:

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}
複製代碼

三、在app的build.gradle中的最上面添加

apply plugin: 'android-aspectjx'
複製代碼

四、在app的build.gradle中的dependencies添加

implementation 'com.github.zhujiang521:AndroidAOP:1.0.1'
複製代碼

五、在你須要使用的方法上面添加上註解便可:

@SingleClick(2000)
    public void btnAop(View view) {
        ToastUtils.showShort("btnAop");
        Log.e(TAG, "btnAop");
    }
複製代碼

「小子,會用了嗎?」

「對了大哥,個人項目中用的是Kotlin啊,我看你寫的都是Java,我那裏面能用嘛!」

「吆喝,還Kotlin呢,放心吧,同樣使用!」

@SingleClick
    override fun onClick(v: View?) {
        if (v != null) {
            when(v.id){
                R.id.btnClick ->{
                    ToastUtils.showShort("哈哈哈")
                    Log.e("哈哈哈","wwww")
                }
            }
        }
    }
複製代碼

總結

「這回能夠了吧?」

「能夠了大哥,嘿嘿,感謝大哥🙏」

「小子,快去改項目吧!」

上面場景模擬的有些尷尬😅,哈哈哈,你們也能夠直接調用。若是文章對你又幫助,歡迎點贊關注,若是文章內容有錯誤之處,歡迎指出,不勝感激。

最後放一下項目的地址:[github.com/zhujiang521…](

相關文章
相關標籤/搜索