【Android】如何防止點擊事件的多次觸發

前言

在項目開發過程中,有時我們需要限制一些組件在短期內只響應一次點擊事件來防止其點擊事件的多次響應,從而使應用的交互更加的友好(如:在網絡不好的狀況下多次點擊登錄,注冊按鈕;多次點擊列表添加按鈕;多次點擊跳轉按鈕等)。
 說到這里,大家的第一反應應該是自定義對應的組件(如:Button,TextView,ImageButton等)來限制點擊,這不失為一個好辦法,但其局限性過大,只適合特定需求的環境下;那么如果我們需要使用的范圍較廣,又不想那么麻煩的通過一個個自定義來解決該如何去做呢——我們可以通過自定義OnClickListener的方式來進行通用的限制,這樣的話,就可以有更廣泛的使用范圍了。


1.使用自定義方式(如:Button)實現

  • onClick何時觸發?
    通過查看Button源碼我們可以得知Button繼承自TextView,而TextView又繼承自View,那么既然點擊事件是通過setOnClickListener來與組件進行綁定的,我們可以到最上層的View.java類中來看下該方法:
    /**
     * Register a callback to be invoked when this view is clicked. If this view is not
     * clickable, it becomes clickable.
     *
     * @param l The callback that will run
     *
     * @see #setClickable(boolean)
     */
    public void setOnClickListener(@Nullable OnClickListener l) {
        if (!isClickable()) {
            setClickable(true);
        }
        getListenerInfo().mOnClickListener = l;
    }

我們可以通過查看mOnClickListener.Onclick在View類的哪些方法里被調用了來判斷到底該重寫哪個方法:

    /**
     * Call this view's OnClickListener, if it is defined.  Performs all normal
     * 通知定義OnClickListener的視圖,執行所有正常
     * actions associated with clicking: reporting accessibility event, playing
     * 與單擊相關的操作:報告可訪問事件,播放
     * a sound, etc.
     * 聲音等
     *
     * @return True there was an assigned OnClickListener that was called, false
     *         otherwise is returned.
     */
    public boolean performClick() {
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this);
            result = true;
        } else {
            result = false;
        }

        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
        return result;
    }

    /**
     * Directly call any attached OnClickListener.  Unlike {@link #performClick()},
     * 直接調用任何附加的OnClickListener。與{@link #performClick()}不同的是,
     * this only calls the listener, and does not do any associated clicking
     * 這里只調用監聽器,不執行任何關聯的單擊操作,
     * actions like reporting an accessibility event.
     * 如報告可訪問事件。
     *
     * @return True there was an assigned OnClickListener that was called, false
     *         otherwise is returned.
     */
    public boolean callOnClick() {
        ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            li.mOnClickListener.onClick(this);
            return true;
        }
        return false;
    }

通過查閱上述代碼我們可以得知,當點擊行為發生時,會先執行performClick()然后觸發對應組件的onClick(View v)回調。

  • 如何攔截onClick的觸發?
    通過對View.jave類中OnClickListener事件監聽器使用的了解,我們知道可以通過重寫自定義組件的performClick()方法來限制OnClick(View v)方法的多次觸發,示例代碼如下:
public class NoMultiClickButton extends Button {

        // 兩次點擊按鈕之間的最小點擊間隔時間(單位:ms)
        private static final int MIN_CLICK_DELAY_TIME = 3000;
        // 最后一次點擊的時間
        private long lastClickTime;

        public NoMultiClickButton(Context context) {
            super(context);
        }

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

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

        @Override
        public boolean performClick() {
            long currentTime = System.currentTimeMillis();
            if (currentTime - lastClickTime > MIN_CLICK_DELAY_TIME) {// 兩次點擊的時間間隔大于最小限制時間,則觸發點擊事件
                lastClickTime = currentTime;
                return super.performClick();
            } else {// 否則,攔截繼續下發的事件
                return false;
            }
        }
}
  • 如何使用
    自定義的Button實際上與我們平時的使用方式一致,示例代碼如下:
NoMultiClickButton noMultiClickButton = (NoMultiClickButton) findViewById(R.id.btn_no_multi);
noMultiClickButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.e(TAG, "onclick");
            }
        });

2.自定義OnClickListener事件監聽

1.自定義NoMultiClickListener繼承View.OnClickListener

public abstract class NoMultiClickListener implements View.OnClickListener {

    @Override
    public void onClick(View v) {
    }

}

2.在Onclick中限制多次點擊

    // 兩次點擊按鈕之間的最小點擊間隔時間(單位:ms)
    private static final int MIN_CLICK_DELAY_TIME = 3000;
    // 最后一次點擊的時間
    private long lastClickTime;

    @Override
    public void onClick(View v) {// 限制多次點擊
        long currentTime = System.currentTimeMillis();
        if (currentTime - lastClickTime > MIN_CLICK_DELAY_TIME) {// 兩次點擊的時間間隔大于最小限制時間,則觸發點擊事件
            lastClickTime = currentTime;
            // 這里觸發點擊事件
        }
    }
  • 創建觸發點擊事件的回調
public abstract class NoMultiClickListener implements View.OnClickListener {

    // 兩次點擊按鈕之間的最小點擊間隔時間(單位:ms)
    private static final int MIN_CLICK_DELAY_TIME = 3000;
    // 最后一次點擊的時間
    private long lastClickTime;

    @Override
    public void onClick(View v) {// 限制多次點擊
        long currentTime = System.currentTimeMillis();
        if (currentTime - lastClickTime > MIN_CLICK_DELAY_TIME) {// 兩次點擊的時間間隔大于最小限制時間,則觸發點擊事件
            lastClickTime = currentTime;
            onNoMultiClick(v);
        }
    }

    /**
     * 點擊事件(相當于@link{android.view.View.OnClickListener})
     *
     * @param v 使用該限制點擊的View
     */
    public abstract void onNoMultiClick(View v);

}
  • 使用示例
btnLogin.setOnClickListener(new NoMultiClickListener() {
            @Override
            public void onNoMultiClick(View v) {
                if (TextUtils.isEmpty(editTextPhoe.getText().toString())) {
                    showToast("輸入的手機號不能為空!");
                }
            }
        });

3.解決多個View共用一個NoMultiClickListener監聽器引發的問題
  當我們使用多個控件綁定同一個NoMultiClickListener監聽器的時候,我們會發現點擊一個觸發單擊事件后,再點擊其它綁定該監聽的組件就觸發不了單擊事件了!!!

一臉懵逼.jpg

  看到這里。。。相信大家都已經想好解決辦法了吧。。其實上面已經實現了對一個控件的限制,那么我們只需要給所有控件單獨綁定上最后的點擊時間然后根據view.getId()取值按照上述邏輯判斷即可,修訂后的具體代碼如下:

public abstract class NoMultiClickListener implements View.OnClickListener {

    // 兩次點擊按鈕之間的最小點擊間隔時間(單位:ms)
    private static final int MIN_CLICK_DELAY_TIME = 3000;
    // 記錄所有綁定該監聽器View的最后一次點擊時間
    private SparseArray<Long> lastClickViewArray = new SparseArray<>();

    @Override
    public void onClick(View v) {// 限制多次點擊
        long currentTime = System.currentTimeMillis();
        long lastClickTime = lastClickViewArray.get(v.getId(), -1L);// 獲取該view最后一次的點擊時間,默認為-1

        if (currentTime - lastClickTime > MIN_CLICK_DELAY_TIME) {// 兩次點擊的時間間隔大于最小限制時間,則觸發點擊事件
            lastClickViewArray.put(v.getId(), currentTime);
            onNoMultiClick(v);
        }
    }

    /**
     * 點擊事件(相當于@link{android.view.View.OnClickListener})
     *
     * @param v 使用該限制點擊的View
     */
    public abstract void onNoMultiClick(View v);

}
本文動力源自絲襪.jpg
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容