為自定義的View新增長按事件

weixin_34391854發表於2014-02-20

以前開發畫板元件時,要新增一個長按監聽事件,這個畫板實際上就是繼承自View的一個自定義元件。

首先,設定好長按事件發生時要觸發的操作:

  private class LongPressRunnable implements Runnable {

        private int x, y;

        public void setPressLocation(float x, float y) {
            this.x = (int) x;
            this.y = (int) y;
        }

        @Override
        public void run() {
            Log.i("發生長按事件,位置在:" + x + "、" + y);
        }

    }

接下來,編寫自定義的View,重點在覆寫dispatchTouchEvent(MotionEvent)方法:

public class MyView extends View {

        ...

    /**
     * 當長按事件發生時,要觸發的任務
     */
    private LongPressRunnable longPressRunnable = new LongPressRunnable();

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {

        if (longPressRunnable != null) {

            //TODO 這裡可以增加一些規則,比如:模糊XY的判定,使長按更容易觸發

            removeCallbacks(longPressRunnable);

            if (event.getAction() == MotionEvent.ACTION_DOWN
                    && event.getPointerCount() == 1) {
                postCheckForLongTouch(event.getX(), event.getY());
            }
        }

        return true;
    }

    private void postCheckForLongTouch(float x, float y) {

        longPressRunnable.setPressLocation(x, y);
       postDelayed(longPressRunnable,
500); } }

原理很簡單,長按事件的本質是:手指觸控某個點保持不移動,也就是touch的時候僅僅發生ACTION_DOWN事件,不發生ACTION_MOVE和ACTION_UP事件,這樣經過一定時間(這裡是500毫秒)則成功觸發長按事件。

所以一個長按事件的週期是On Touch Down ---> 500ms ---> On Long Press。

接下來分析上面的實現:當我們收到Touch事件時,不管是什麼事件,先刪除上一次設定好的longPressRunnable,

removeCallbacks(longPressRunnable);

這意味著上一次觸控週期沒有觸發長按操作。

然後判斷事件型別,如果是“單指ACTION_DOWN”事件則表明有可能會觸發長按操作,那麼我們通過View.postDelayed()方法將longPressRunnable放進這個View所線上程的任務佇列中,並延遲500毫秒執行,

if (event.getAction() == MotionEvent.ACTION_DOWN && event.getPointerCount() == 1) {
     postCheckForLongTouch(event.getX(), event.getY());
}

 

private void postCheckForLongTouch(float x, float y) {
    longPressRunnable.setPressLocation(x, y);
    postDelayed(longPressRunnable, 500);
}

 

如果在這500毫秒內,使用者的手指沒有移動或抬起,也就是不會有任何touch事件到來,則該longPressRunnable會在500毫秒後執行,一個長按操作就完成了。

如果在這500毫秒內使用者的手指移動或抬起了,那麼新的touch事件到來,longPressRunnable也會被移除。

以上就是整個實現,這個實現有個需要優化的地方,就是//TODO那裡,由於人的手指並沒有那麼精細,在長按過程中可能會有一點移動,這往往會導致長按失敗,所以我們可以在//TODO那裡新增對ACTION_MOVE事件的處理,忽略一些細微的移動事件,留給讀者自行實現吧:D

相關文章