Android判斷是否快速點擊新思路


背景需求

在用戶提交表單內(nèi)容,請求接口的通用場景下,增刪改查 crud 操作是比較頻繁出現(xiàn)的操作。
對于改查操作來講,并不會引起數(shù)據(jù)上的問題,影響的最多是接口重復調(diào)用;但是對于增刪操作來講,很容易引起數(shù)據(jù)重復添加,刪除異常等問題。
對用戶來講,網(wǎng)絡(luò)延時或者不穩(wěn)定造成請求接口時出現(xiàn)的App反應(yīng)遲緩,下意識的會進行重復提交;或者用戶不小心進行的重復點擊;又或者測試App自動化測試工具導致的短時間內(nèi)大量重復點擊。
這些場景都應(yīng)該在代碼實現(xiàn)的時候應(yīng)該考慮的。

1. “笨”方法實現(xiàn)

說是笨方法,當然也是出現(xiàn)重復提交問題時候,想到的最有效的方法。

1.1 狀態(tài)標記

對于每個點擊按鈕,初始狀態(tài) isPressed = FALSE 進行狀態(tài)判定,點擊一次記錄狀態(tài) isPressed = TURE ,然后進行接口請求或者其它處理,接口請求完成之后初始化該按鈕狀態(tài) isPressed = FALSE

1.2 狀態(tài)標記高級版

Android 中怎樣設(shè)置按鈕不能點擊?

其實,和上面的方法沒什么區(qū)別,只不過借助了** Button **的屬性設(shè)置來完成的。

Button.setEnabled(false);//設(shè)置這個屬性

思路和1.1一致,生了一個局部變量,依然有效!

2. Clever方法實現(xiàn)

其實,對于重復點擊這類問題,只需要一個狀態(tài)標記就能夠解決,但是我們的之前的方法只適用于單個** Button **按鈕,對于多個按鈕,如果我們這么去做,必然是大量的 Ctrl+C 和 Ctrl+V,想想都心累,更別說我還真的這么干過。

如果有一種一勞永逸的方法豈不是很好,對了,然后我們想到了封裝,從** Button 組件內(nèi)部去判斷豈不是美妙,這樣我們只需要替換xml中的 Button 就可以完美的解決大量的按鈕點擊事件。
既然,
Button **按鈕的事件點擊是通過

setOnClickListener(OnClickListener listener)

方法注冊的,我們只需要在注冊的時候,替換新的Listener,加上我們自定義事件判斷就可以搞定。
比如:

public class OnNoRepeatButton extends Button
{
    public ButtonEx(final Context context)
    {
        super(context);
    }

    public ButtonEx(final Context context, final AttributeSet attrs)
    {
        super(context, attrs);
    }

    public ButtonEx(final Context context, final AttributeSet attrs, final int defStyleAttr)
    {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public void setOnClickListener(final OnClickListener listener)
    {
        super.setOnClickListener(new OnNoRepeatClickListener(listener));
    }
}

我們的** OnNoRepeatClickListener **:

/**
 * 防止快速點擊中多次觸發(fā)事件的自定義OnClickListener
 */
public class OnNoRepeatClickListener implements OnClickListener
{
    private final OnClickListener mListener;

    private long lastClickTime = 0;

    public OnNoRepeatClickListener(final OnClickListener listener)
    {
        this.mListener = listener;
    }

    @Override
    public void onClick(final View v)
    {
        synchronized (this)
        {
            //判斷當前點擊時間與上一次點擊時間的間隔
            if (System.currentTimeMillis() - this.lastClickTime > 1000)
            {
                if (this.mListener != null)
                    this.mListener.onClick(v);

                //處理完響應(yīng)事件后記錄時間確保下次響應(yīng)
                this.lastClickTime = System.currentTimeMillis();
            }
        }
    }
}

Note: 代碼中的** 1000 **單位是ms,具體的時間需要根據(jù)需求來定義。

3. Smart方法實現(xiàn)

Clever方法已經(jīng)算是一個相對好的解決思路,但是如果我們的** Textview ,ImageView , View **等組件點擊事件重復需要怎么解決呢?
也需要我們重寫每一個的組件的

setOnClickListener(OnClickListener listener)

來實現(xiàn)。
那么,如果我們在重寫了** Activity/Fragment **中重寫

View.OnClickListener

方法,對點擊事件時間進行判斷,便可以減少對** View **組件的

setOnClickListener(OnClickListener listener)

方法的重寫。

上代碼:

interface IBaseView extends View.OnClickListener {

    /**
     * 初始化數(shù)據(jù)
     *
     * @param bundle 傳遞過來的bundle
     */
    void initData(final Bundle bundle);

    /**
     * 綁定布局
     *
     * @return 布局Id
     */
    int bindLayout();

    /**
     * 初始化view
     */
    void initView(final Bundle savedInstanceState, final View view);

    /**
     * 業(yè)務(wù)操作
     */
    void doBusiness();

    /**
     * 視圖點擊事件
     *
     * @param view 視圖
     */
    void onWidgetClick(final View view);
}

BaseActivity.java

/**
 * <pre>
 *     desc  : Activity基類
 * </pre>
 */
public abstract class BaseActivity extends AppCompatActivity
        implements IBaseView {

    /**
     * 當前Activity渲染的視圖View
     */
    protected View contentView;
    /**
     * 上次點擊時間
     */
    private long lastClick = 0;

    protected BaseActivity mActivity;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mActivity = this;
        Bundle bundle = getIntent().getExtras();
        initData(bundle);
        setBaseView(bindLayout());
        initView(savedInstanceState, contentView);
        doBusiness();
    }

    protected void setBaseView(@LayoutRes int layoutId) {
        setContentView(contentView = LayoutInflater.from(this).inflate(layoutId, null));
    }

    /**
     * 判斷是否快速點擊
     *
     * @return {@code true}: 是<br>{@code false}: 否
     */
    private boolean isFastClick() {
        long now = System.currentTimeMillis();
        if (now - lastClick >= 800) {
            lastClick = now;
            return false;
        }
        return true;
    }

    @Override
    public void onClick(final View view) {
        if (!isFastClick()) onWidgetClick(view);
    }
}

總結(jié)


當然,Smart方法也不是最佳的解決方案,這樣來做需要組件采用

setOnClickListener(this)

調(diào)用方式,讓** Activity/Fragment **統(tǒng)一處理點擊事件的響應(yīng),Clever和Smart的結(jié)合也是一個不錯的選擇。

Have fun , enjoy Coding...
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容