Android系統自帶了長按事件,setOnLongClickListener即可監聽。但是有時候,你不希望用系統的長按事件,比如當希望長按的時間更長一點的時候。這時候就需要自己來定義這個長按事件了。
下面是去年我寫代碼的時候,自定義長按事件的方式:
- package chroya.fun;
- import android.content.Context;
- import android.view.MotionEvent;
- import android.view.View;
- import android.view.ViewConfiguration;
- public class LongPressView1 extends View{
- private int mLastMotionX, mLastMotionY;
- //是否移動了
- private boolean isMoved;
- //是否釋放了
- private boolean isReleased;
- //計數器,防止多次點擊導致最后一次形成longpress的時間變短
- private int mCounter;
- //長按的runnable
- private Runnable mLongPressRunnable;
- //移動的閾值
- private static final int TOUCH_SLOP = 20;
- public LongPressView1(Context context) {
- super(context);
- mLongPressRunnable = new Runnable() {
- @Override
- public void run() {
- mCounter--;
- //計數器大於0,說明當前執行的Runnable不是最后一次down產生的。
- if(mCounter>0 || isReleased || isMoved) return;
- performLongClick();
- }
- };
- }
- public boolean dispatchTouchEvent(MotionEvent event) {
- int x = (int) event.getX();
- int y = (int) event.getY();
- switch(event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mLastMotionX = x;
- mLastMotionY = y;
- mCounter++;
- isReleased = false;
- isMoved = false;
- postDelayed(mLongPressRunnable, ViewConfiguration.getLongPressTimeout());
- break;
- case MotionEvent.ACTION_MOVE:
- if(isMoved) break;
- if(Math.abs(mLastMotionX-x) > TOUCH_SLOP
- || Math.abs(mLastMotionY-y) > TOUCH_SLOP) {
- //移動超過閾值,則表示移動了
- isMoved = true;
- }
- break;
- case MotionEvent.ACTION_UP:
- //釋放了
- isReleased = true;
- break;
- }
- return true;
- }
- }
代碼里注釋的比較清楚。主要思路是在down的時候,讓一個Runnable一段時間后執行,如果時間到了,沒有移動超過定義的閾值,也沒有釋放,則觸發長按事件。在真實環境中,當長按觸發之后,還需要將后來的move和up事件屏蔽掉。此處是示例,就略去了。
下面講講第二種方式:
- package chroya.fun;
- import android.content.Context;
- import android.view.MotionEvent;
- import android.view.View;
- import android.view.ViewConfiguration;
- public class LongPressView2 extends View{
- private int mLastMotionX, mLastMotionY;
- //是否移動了
- private boolean isMoved;
- //長按的runnable
- private Runnable mLongPressRunnable;
- //移動的閾值
- private static final int TOUCH_SLOP = 20;
- public LongPressView2(Context context) {
- super(context);
- mLongPressRunnable = new Runnable() {
- @Override
- public void run() {
- performLongClick();
- }
- };
- }
- public boolean dispatchTouchEvent(MotionEvent event) {
- int x = (int) event.getX();
- int y = (int) event.getY();
- switch(event.getAction()) {
- case MotionEvent.ACTION_DOWN:
- mLastMotionX = x;
- mLastMotionY = y;
- isMoved = false;
- postDelayed(mLongPressRunnable, ViewConfiguration.getLongPressTimeout());
- break;
- case MotionEvent.ACTION_MOVE:
- if(isMoved) break;
- if(Math.abs(mLastMotionX-x) > TOUCH_SLOP
- || Math.abs(mLastMotionY-y) > TOUCH_SLOP) {
- //移動超過閾值,則表示移動了
- isMoved = true;
- removeCallbacks(mLongPressRunnable);
- }
- break;
- case MotionEvent.ACTION_UP:
- //釋放了
- removeCallbacks(mLongPressRunnable);
- break;
- }
- return true;
- }
- }
思路跟第一種差不多,不過,在移動超過閾值和釋放之后,會將Runnable從事件隊列中remove掉,長按事件也就不會再觸發了。源碼中實現長按的原理也基本如此。

