以前開發畫板元件時,要新增一個長按監聽事件,這個畫板實際上就是繼承自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