Android中如何優(yōu)雅的處理重復(fù)點(diǎn)擊實(shí)例代碼
問(wèn)題
有時(shí)候有些操作是防止用戶在一次響應(yīng)結(jié)束中再響應(yīng)下一個(gè)。但有些測(cè)試用戶就要猛點(diǎn),狂點(diǎn)。像這種惡意就要進(jìn)行防止。
比如在客戶端中,一些按鈕一般是需要避免重復(fù)點(diǎn)擊的,比如:購(gòu)買丶支付丶確定丶提交丶點(diǎn)贊丶收藏等等場(chǎng)景,這些場(chǎng)景短時(shí)間內(nèi)的重復(fù)點(diǎn)擊會(huì)引發(fā)一些問(wèn)題.
下面話不多說(shuō)了,來(lái)一起看看詳細(xì)的介紹吧
以前的處理方式
可能是采用手動(dòng)記錄最后的點(diǎn)擊時(shí)間,再通過(guò)計(jì)算時(shí)間間隔來(lái)判斷是否重復(fù)點(diǎn)擊
private long mLastClickTime = 0; public static final int TIME_INTERVAL = 1000; private Button mButton; private void initView() { mButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (System.currentTimeMillis() - mLastClickTime >= TIME_INTERVAL) { //to do mLastClickTime = System.currentTimeMillis(); } else { Toast.makeText(getActivity(), "請(qǐng)勿重復(fù)點(diǎn)擊", Toast.LENGTH_LONG).show(); } } }); }
或者封裝一下采用抽象處理
public abstract class IClickListener implements View.OnClickListener { private long mLastClickTime = 0; public static final int TIME_INTERVAL = 1000; @Override public final void onClick(View v) { if (System.currentTimeMillis() - mLastClickTime >= TIME_INTERVAL) { onIClick(v); mLastClickTime = System.currentTimeMillis(); } else { onAgain(v); } } protected abstract void onIClick(View v); protected void onAgain(View v) { } }
使用(無(wú)需提醒重復(fù)點(diǎn)擊)
mButton.setOnClickListener(new IClickListener() { @Override protected void onIClick(View v) { } });
或者(需提醒重復(fù)點(diǎn)擊)
mButton.setOnClickListener(new IClickListener() {
@Override
protected void onIClick(View v) {
}
@Override
protected void onAgain(View v) {
}
});
可以看到經(jīng)過(guò)封裝之后,使用起來(lái)還是很方便的,但是有幾個(gè)缺點(diǎn)
- 侵入性過(guò)大-OnClickListener全部替換為子類IClickListener
- 不可逆-不能很方便的還原為OnClickListener,因?yàn)椴皇峭瑐€(gè)回調(diào)
- 如果是第三方控件則無(wú)法處理重復(fù)點(diǎn)擊
- 只能寫成內(nèi)部類方式-由于單繼承特性,我們只能內(nèi)部類回調(diào),代碼不美觀
優(yōu)雅的處理方式
重復(fù)點(diǎn)擊的問(wèn)題其實(shí)是如何動(dòng)態(tài)控制原有的點(diǎn)擊事件是否產(chǎn)生,而不是在原有的點(diǎn)擊事件上增強(qiáng)功能;結(jié)合設(shè)計(jì)模式可以知道,代理模式可以很好的處理這種問(wèn)題,而不是繼承.
代理
public class ClickProxy implements View.OnClickListener { private View.OnClickListener origin; private long lastclick = 0; private long timems = 1000; public ClickProxy(View.OnClickListener origin) { this.origin = origin; } @Override public void onClick(View v) { if (System.currentTimeMillis() - lastclick >= timems) { origin.onClick(v); lastclick = System.currentTimeMillis(); } } }
原先的點(diǎn)擊事件
mButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { //to do } });
代理使用
mButton.setOnClickListener(new ClickProxy(new View.OnClickListener() { @Override public void onClick(View v) { //to do } }));
可以看到,原有代碼邏輯沒(méi)有改動(dòng),只是添加了代理類,這樣大大減小了侵入性
當(dāng)然還可以擴(kuò)展一下,提供重復(fù)點(diǎn)擊的回調(diào)和自定義間隔時(shí)間,增加一個(gè)構(gòu)造函數(shù)
public class ClickProxy implements View.OnClickListener { private View.OnClickListener origin; private long lastclick = 0; private long timems = 1000; //ms private IAgain mIAgain; public ClickProxy(View.OnClickListener origin, long timems, IAgain again) { this.origin = origin; this.mIAgain = again; this.timems = timems; } public ClickProxy(View.OnClickListener origin) { this.origin = origin; } @Override public void onClick(View v) { if (System.currentTimeMillis() - lastclick >= timems) { origin.onClick(v); lastclick = System.currentTimeMillis(); } else { if (mIAgain != null) mIAgain.onAgain(); } } public interface IAgain { void onAgain();//重復(fù)點(diǎn)擊 } }
如何處理第三方View內(nèi)部的點(diǎn)擊事件
可能我們使用一個(gè)自定義控件,他的內(nèi)部已經(jīng)消費(fèi)了點(diǎn)擊事件,但是需要避免重復(fù)點(diǎn)擊,我們不可能去改內(nèi)部的代碼,也不能重新設(shè)置點(diǎn)擊事件,那樣會(huì)丟失內(nèi)部的處理邏輯;這時(shí)可以采用反射的處理方式,再結(jié)合代理來(lái)實(shí)現(xiàn)無(wú)縫替換
//提供一個(gè)靜態(tài)方法 public class ClickFilter { public static void setFilter(View view) { try { Field field = View.class.getDeclaredField("mListenerInfo"); field.setAccessible(true); Class listInfoType = field.getType(); Object listinfo = field.get(view); Field onclickField = listInfoType.getField("mOnClickListener"); View.OnClickListener origin = (View.OnClickListener) onclickField.get(listinfo); onclickField.set(listinfo, new ClickProxy(origin)); } catch (Exception e) { e.printStackTrace(); } } }
使用:
private StateButton mStateButton;//自定義控件 private void initView() { ClickFilter.setFilter(mStateButton); }
這種動(dòng)態(tài)替換的方式同樣適合普通場(chǎng)景,在設(shè)置點(diǎn)擊事件后,都可以通過(guò)設(shè)置該過(guò)濾器來(lái)處理重復(fù)點(diǎn)擊(包括butterknife等注解綁定的點(diǎn)擊事件)
最后
Ok.以上就是討論如何優(yōu)雅處理重復(fù)點(diǎn)擊的全部?jī)?nèi)容,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
Android適配利用webview加載后圖片顯示過(guò)大的問(wèn)題解決
這篇文章主要給大家介紹了關(guān)于Android適配利用webview加載后圖片顯示過(guò)大問(wèn)題的解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)各位Android開(kāi)發(fā)者們具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07android的消息處理機(jī)制(圖文+源碼分析)—Looper/Handler/Message
這篇文章寫的非常好,深入淺出;android的消息處理機(jī)制(圖+源碼分析)—Looper,Handler,Message是一位大三學(xué)生自己剖析的心得,感興趣的朋友可以了解下哦,希望對(duì)你有所幫助2013-01-01Android 自定義TextView實(shí)現(xiàn)文本內(nèi)容自動(dòng)調(diào)整字體大小
本文主要介紹了Android 自定義TextView實(shí)現(xiàn)文本內(nèi)容自動(dòng)調(diào)整字體大小以適應(yīng)TextView的大小的方法。具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧2017-03-03Android使用CountDownTimer模擬短信驗(yàn)證倒計(jì)時(shí)
這篇文章主要為大家詳細(xì)介紹了Android使用CountDownTimer模擬短信驗(yàn)證倒計(jì)時(shí),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07VS Code開(kāi)發(fā)React-Native及Flutter 開(kāi)啟無(wú)線局域網(wǎng)安卓真機(jī)調(diào)試問(wèn)題
這篇文章主要介紹了VS Code開(kāi)發(fā)React-Native,F(xiàn)lutter 開(kāi)啟無(wú)線局域網(wǎng)安卓真機(jī)調(diào)試,需要的朋友可以參考下2020-04-04Android DragImageView實(shí)現(xiàn)下拉拖動(dòng)圖片放大效果
這篇文章主要為大家詳細(xì)介紹了Android DragImageView實(shí)現(xiàn)下拉拖動(dòng)圖片放大效果,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-12-12Android Activity打開(kāi)后被應(yīng)用快照遮住的問(wèn)題
這篇文章主要介紹了Android Activity打開(kāi)后被應(yīng)用快照遮住的問(wèn)題,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-01-01android中Glide實(shí)現(xiàn)加載圖片保存至本地并加載回調(diào)監(jiān)聽(tīng)
本篇文章主要介紹了android中Glide實(shí)現(xiàn)加載圖片保存至本地并加載回調(diào)監(jiān)聽(tīng),具有一定的參考價(jià)值,有興趣的可以了解一下2017-09-09