View事件的分發機制由三個方法共同完成,這三個方法是: public boolean dispatchTouchEvent(MotionEvent ev); public boolean onInterceptTouchEvent(MotionEvent ev); public boolean o ...
View事件的分發機制由三個方法共同完成,這三個方法是:
public boolean dispatchTouchEvent(MotionEvent ev);
public boolean onInterceptTouchEvent(MotionEvent ev);
public boolean onTouchEvent(MotionEvent ev);
它們之間的關係可以用下麵的偽代碼表示:
public boolean dispatchTouchEvent(MotionEvent ev) { boolean consume=false;
if(onInterceptTouchEvent(ev)) { consume=onTouchEvent(ev); } else { consume=child.dispatchTouchEvent(ev); } return consume; }
從上面的偽代碼中可以看出點擊事件的傳遞規則是這樣的:
點擊事件的傳遞是由外向內的,最先接受的到事件的是最外層的ViewGroup,這時候會調用dispatchTouchEvent方法(如果能夠接收到事件,那麼此方法一定會被調用),如果onInterceptTouchEvent方法返回的是true,那麼就說明它要攔截這個事件,於是這個事件就交給該ViewGroup處理,也就是調用onTouchEvent方法。
相反的,如果onInterceptTouchEvent的方法返回的是false,那麼就說明該ViewGroup不攔截事件,事件會傳遞給它的子元素,子元素的dispatchTouchEvent方法會被調用,以此類推一直進行下去,直到事件最終被處理。
如果該事件已經交給子view處理,但是子view的onTouchEvent方法返回了false(沒有處理事件,或者處理事件失敗了),那麼父容器的onTouchEvent就會被調用(我們可以形象的認為下級處理事件失敗,那麼就由上級進行處理),以此類推上去,如果所有的元素都不處理這個事件,那麼這個事件最終會傳遞給activity,activity的onTouchEvent方法會被調用。
事件分發的時候傳遞順序為Activity->Window->View.
由上面的分析過程我們可以知道上面三個方法分別的作用是什麼:
dispatchTouchEvent:是用來分發事件的,如果事件能夠傳遞到該view,那麼該view的dispatchTouchEvent方法一定會被調用。
onInterceptTouchEvent:用來判斷是否攔截事件,返回true表示攔截,返回false表示不攔截。一旦事件被該view攔截,那麼同一個事件序列中該方法不會再被調用。
onTouchEvent:用來處理事件,返回true表示消耗,false表示不消耗。如果不消耗,那麼同一個事件序列中當前的view無法再接收到事件。
何為同一個事件序列:手指觸摸屏幕的那一刻到手指離開屏幕的那一刻,這個過程中產生的一系列事件。
(例如當你在滑動的時候:ACTION_DOWN->ACTION_MOVE->ACTION_MOVE...->ACTION_UP)
onTouchEvent,OnTouchListener,OnClickListener的優先順序:
OnTouchListener>onTouchEvent>OnClickListener
如何說呢:如果該view處理事件的時候設置了OnTouchListener,那麼OnTouchListener方法裡面的onTouch會被回調,若是onTouch返回了true,那麼onTouchEvent不會被調用,反之onTouchEvent會被調用,此時若是設置了OnClickListener,那麼在onTouchEvent中onClick方法會被調用。
有幾個需要註意的小點:
1、一個view一旦開始處理事件,若它不消耗ACTION_DOWN事件(onTouchEvent返回了false),那麼同一個事件序列的其他事件都不會再交給它處理,事件會交到父元素去處理(父元素的onTouchEvent會被調用)。若是該view消耗了ACTION_DOWN事件,但是不消耗其他事件,那麼事件就會消失,最終這些消失的點擊事件就會傳遞給Activity處理。
2、ViewGroup預設不攔截所有的事件,它的onInterceptTouchEvent預設返回了false。
3、view沒有onInterceptTouchEvent方法,一旦有事件傳遞給它,那麼它的onTouchEvent方法就會被帶調用。
4、view的onTouchEvent方法預設返回true,也就會說view預設會消耗事件,除非它是不了點擊的,也就是它的clickable和longClickable同時被設置為false。事實上view的longClickable屬性預設為false,clickable則要分情況,比如Button的clickable屬性預設為true,TextView的clickable屬性預設為false。
5、view的enable屬性不會影響onTouchEvent的預設返回值。即使其為disable狀態,只要它的clickable或者longClickable有一個為true,那麼它的onTouchEvent就返回true。
6、onClick會發生的前提是當前的View是可點擊的,並且它收到了down和up事件。