<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          一行代碼解決重復(fù)點(diǎn)擊問題

          共 5585字,需瀏覽 12分鐘

           ·

          2020-08-06 01:59

          作者:Zhujiang
          鏈接:https://juejin.im/post/5ea66d64f265da480836d2b2

          拋出問題

          “大哥,有個(gè)問題想問你!”

          “哎,說吧(內(nèi)心戲:咋又來了。。。準(zhǔn)沒好事!)”

          “我的一個(gè)頁面中有一個(gè)查詢按鈕,點(diǎn)擊就會(huì)發(fā)出網(wǎng)絡(luò)請(qǐng)求,等待返回結(jié)果后更新數(shù)據(jù)。”

          “這不挺好的嘛!有啥問題啊?”

          “對(duì),我也覺得沒問題,但測(cè)試不按套路出牌啊,測(cè)試那邊的網(wǎng)絡(luò)不太好,她點(diǎn)擊按鈕之后由于網(wǎng)絡(luò)比較慢就快速多點(diǎn)擊了幾下,然后。。。”

          “然后怎么了?ANR了吧?”

          “你咋知道的大哥?”

          “來吧,幫您看看吧!”

          日常開發(fā)中肯定遇到過這種情況,接下來咱們就來看看該怎么解決這種問題。


          第一種:彈窗等待


          “小子,過來,你看啊,你可以這樣,當(dāng)你點(diǎn)擊了按鈕之后就彈出一個(gè)對(duì)話框,設(shè)置成不能關(guān)閉,等網(wǎng)絡(luò)請(qǐng)求完成之后再將對(duì)話框關(guān)閉了就行”

          “這是一種方式,但我該怎么寫呢?”

          “哎,給你寫一下例子吧:照著下面代碼寫就行”


          public void btnDialog(View view) {        ProgressDialog progressDialog = new ProgressDialog(this);        progressDialog.setTitle("等待");        progressDialog.setMessage("等待內(nèi)容");        //progressDialog.setCanceledOnTouchOutside(false);        progressDialog.show();    }


          上面代碼很簡(jiǎn)單,這只是一種思路,點(diǎn)擊按鈕后可以彈出對(duì)話框不讓用戶進(jìn)行操作(注釋的那一行代碼就是禁止用戶點(diǎn)擊的),當(dāng)請(qǐng)求完成之后再將對(duì)話框關(guān)閉。

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


          第二種:禁止點(diǎn)擊


          “大哥,我覺得彈出對(duì)話框不太好,會(huì)讓用戶很反感,還有別的方式嗎?”

          “行了,早就準(zhǔn)備好和你說了,還不止一種呢!這種方式更簡(jiǎn)單了,只需要設(shè)置按鈕是否可以點(diǎn)擊就行,當(dāng)用戶點(diǎn)擊后設(shè)置按鈕不可點(diǎn)擊,請(qǐng)求完成之后再設(shè)置可以點(diǎn)擊就行了,這個(gè)不用我寫代碼了吧?“

          ”嘿嘿,這個(gè)不用,你剛才說還有好幾種,說來聽聽啊!“


          第三種:時(shí)間判斷


          這種方式比上面的稍微麻煩點(diǎn),但還是很簡(jiǎn)單。

          具體操作就是定義兩個(gè)變量,一個(gè)為上次點(diǎn)擊時(shí)間,一個(gè)為點(diǎn)擊的間隔時(shí)間。


          // 上次點(diǎn)擊時(shí)間private long lastClickTime = 0L;// 兩次點(diǎn)擊間隔時(shí)間(毫秒)private static final int FAST_CLICK_DELAY_TIME = 1500;

          在點(diǎn)擊時(shí)進(jìn)行判斷就可以了,來看一下代碼吧:


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


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

          “先別高興的太早了!”


          第四種:AOP實(shí)現(xiàn)


          “大哥,你剛才說我高興的太早了是為啥啊?“

          ”你只有一個(gè)頁面的話這樣寫肯定是沒有問題的,但是如果有多個(gè)頁面都有防止按鈕重復(fù)點(diǎn)擊的需求呢?每個(gè)頁面都定義一遍啊?“

          ”呃呃,你說的對(duì),大哥,那應(yīng)該怎么辦呢?“

          ”你知道AOP嗎?接下來我要說的就和它有關(guān)“

          ”AOP?那是什么鬼?我知道OOP!“

          ”不錯(cuò)啊小子,還知道OOP,OOP就是面向?qū)ο螅鳤OP就是面向過程。“


          AOP為Aspect OrientedProgramming的縮寫,意為面向切面編程。所謂的面向切面編程其實(shí)是對(duì)業(yè)務(wù)邏輯又進(jìn)行了進(jìn)一步的抽取,將多種業(yè)務(wù)邏輯中的公用部分抽取出來做成一種服務(wù)(比如日志記錄,性能統(tǒng)計(jì)等),從而實(shí)現(xiàn)代碼復(fù)用。另外這種服務(wù)通過配置可以動(dòng)態(tài)的給程序添加統(tǒng)一控制,利用AOP可以對(duì)業(yè)務(wù)邏輯的各個(gè)部分進(jìn)行分離,從而使得業(yè)務(wù)邏輯各部分之間的耦合度降低。

          AOP并不是Android中的產(chǎn)物,而是Java中的,Android官方并沒有提供,所以想使用AOP首先要導(dǎo)入可以實(shí)現(xiàn)AOP的三方庫(kù):

          在項(xiàng)目級(jí)別的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'


          完事之后點(diǎn)擊sync Now更新一下。

          因?yàn)橄朐谒械胤蕉寄苁褂茫詠矶x一個(gè)注解:


          /** * 實(shí)現(xiàn)防止按鈕連續(xù)點(diǎn)擊 * @author jiang zhu on 2020/4/19 */@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface SingleClick {    /* 點(diǎn)擊間隔時(shí)間 */    long value() default 1500;}


          注解大家應(yīng)該都使用過,運(yùn)行時(shí)就不說了,使用范圍定義的是方法上,有一個(gè)參數(shù)為間隔時(shí)間,這個(gè)參數(shù)意思其實(shí)和第三種方案中的類似。

          然后就需要定義一個(gè)類,里面實(shí)現(xiàn)AOP的具體操作:


          @Aspectpublic class SingleClickAspect {}


          然后定義切點(diǎn),標(biāo)記切點(diǎn)為所有被@SingleClick的方法:


          @Pointcut("execution(@com.zj.singclick.SingleClick * *(..))")public?void?methodAnnotated()?{}


          這里要注意包名和類名一定要寫對(duì)。

          接下來定義一個(gè)切面方法,包裹著切點(diǎn)方法:


          @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(); } }


          來簡(jiǎn)單分析下,首先看注解中定義了剛才定義的切點(diǎn)方法,然后取出方法的參數(shù),再取出方法的注解,然后調(diào)用

          isFastDoubleClick方法判斷是否為快速點(diǎn)擊事件,如果是什么都不干,如果不是則執(zhí)行原方法中的內(nèi)容。

          下面貼一下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和間隔時(shí)間雙重判斷,即使一個(gè)頁面的多個(gè)按鈕都可區(qū)分。

          “大哥,停停停,都給我說懵了,這寫完這個(gè)該怎么用啊!”

          “別著急,下面就教你該怎么使用!”

          使用很簡(jiǎn)單,只需要在點(diǎn)擊時(shí)間方法上加上咱們自定義的注解就行了,還可以設(shè)置間隔時(shí)間,如果不寫的話就是默認(rèn)值,上面也寫了,默認(rèn)值就是1500毫秒。


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


          “是不是很簡(jiǎn)單啊?”

          “大哥,我也不想寫這一大堆,我只想用,你能封裝成一個(gè)庫(kù)嗎?我用的時(shí)候直接調(diào)用就行!”

          “哎,行吧,我封裝一下。。。。”


          第四種封裝


          “我已經(jīng)將上面第四種方式封裝為了一個(gè)庫(kù)并上傳到了JitPack庫(kù),下面給你說一下使用方法吧!”

          使用方法很簡(jiǎn)單,需要幾步配置,配置完成之后直接添加注解即可使用,下面是配置方法:1、在項(xiàng)目的build.gradle中的buildscript中的dependencies添加:


          dependencies {        ...        classpath 'com.hujiang.aspectjx:gradle-android-plugin-aspectjx:2.0.4' }


          2、在項(xiàng)目的build.gradle中的allprojects中的repositories添加:


          allprojects {    repositories {        ...        maven { url 'https://jitpack.io' }    }}


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


          apply plugin: 'android-aspectjx'


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


          implementation 'com.github.zhujiang521:AndroidAOP:1.0.1'


          5、在你需要使用的方法上面添加上注解即可:


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


          “小子,會(huì)用了嗎?”

          “對(duì)了大哥,我的項(xiàng)目中用的是Kotlin啊,我看你寫的都是Java,我那里面能用嘛!”

          “吆喝,還Kotlin呢,放心吧,一樣使用!”


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

          總結(jié)


          “這回可以了吧?”

          “可以了大哥,嘿嘿,感謝大哥?”

          “小子,快去改項(xiàng)目吧!”

          上面場(chǎng)景模擬的有些尷尬?,哈哈哈,大家也可以直接調(diào)用。

          項(xiàng)目地址:https://github.com/zhujiang521/AndroidAOP


          ? 開發(fā)者全社區(qū)?

          5T技術(shù)資源大放送!包括但不限于:Android,Python,Java,大數(shù)據(jù),人工智能,AI等等。關(guān)注公眾號(hào)后回復(fù)「2T」,即可免費(fèi)獲取!
          瀏覽 63
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产激情123区 | 日韩一级黄片 | 人人擦人人 | 大香蕉综合在线观看 | 69欧美视频 |