一.事件由activity開始傳遞,activity的dispatchTouchEvent代碼如下:
public boolean dispatchTouchEvent(MotionEvent ev){
if(ev.getAction() == MotionEvent.Action_Down){
onUserInteraction();
}
if(getWindow().superDispatchTouchEvent(ev){
return true;
}
return onTouchEvent(ev);
}
由於上面的代碼我們可以知道,會直接調用window的superDispatchTouchEvent方法,當該方法返回false的時候,纔會調用activity的onTouchEvent方法進行處理
window的實現類是PhoneWindow,實現方法如下:
public boolean superDispatchTouchEvent(MotionEvent event){
return mDecor.superDispatchTouchEvent(event);
}
由上代碼可知,直接調用了DecorView的superDispatchTouchEvent方法,點擊事件由此進入view體系進行分發。
二.在ViewGroup的dispatchTouchEvent方法中:
final boolean intercepted;
if(actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null){
final boolean disallowIntercept = ( mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if(!disallowIntercept){
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action);
}else{
intercepted = false;
}
}else{
intercepted = true;
}
如果action_down事件由ViewGroup的子元素消耗,則mFirstTouchTarget != null
所以當mFirstTouchTarget == null時,表示ViewGroup的子元素不消耗action_down事件
這時action_move,action_up事件都會返回intercepted = true ,即該事件由ViewGroup處理
如果爲action_down事件或者mFirstTouchTarget!= null 時,
會判讀FLAG_DISALLOW_INTERCEPT標誌是否爲0,FLAG_DISALLOW_INTERCEPT由子view的requestDisallowInterTouchEvent方法設置,當設置爲true時才
intercepted = false,ViewGroup將無法攔截事件
但如果事件爲action_down,FLAG_DISALLOW_INTERCEPT會被重置,一定會調用onInterceptTouchEvent方法判斷是否進行攔截
三.交給view處理
如果intercepted = false,表示viewGroup不攔截事件,將交給子view進行處理
他會遍歷所有的子元素,
判斷子元素是否在播動畫已經點擊事件是否落在子元素裏,如果滿足該條件,那麼事件就傳給他處理。
他會調用子元素的dispatchTouchEvent方法,
如果子元素的dispatchTouchEvent返回true,那麼mFirstTouchTarget就會被賦值並終止對子view的遍歷。
newTouchTarget = addTouchTarget(child,idBitsToAssign);
alreadyDispatchedToNewTouchTarget = true;
break;
private TouchTarget addTouchTarget(View child,int pointerIdBits){
TouchTarget target = TouchTarget.obtain(child,pointerIdBits);
target.next = mFirstTouchTarget;
mFirstTouchTarget = target;
return target;
如果返回false,則會將事件分發給下一個子元素。
如果ViewGroup沒有 子元素,或者子元素處理了點擊事件但它的dispatchTouchEvent返回false
ViewGroup會自己處理點擊事件
if(mFirstTouchTarget == null){
handled = dispatchTransformedTouchEvent(ev,canceled,null,TouchTarget.ALL_POINTER_IDS);
}
四。View對點擊事件的處理
public boolean dispatchTouchEvent(MotionEvent event){
...
if(onFilterTouchEventForSecurity(event){
ListenterInfor li = mListenerInfo;
if(li != null && li.mOnTouchListener != null&&(mViewFlags & ENABLED_MASK)==ENABLED &&li.mOnTouchListener.onTouch(this,event){
result = true;
}
if(!result && onTouchEvent(event){
result = true;
}
}
...
return result;
}
}
如果設置了mOnTouchListener,就會調用onTouch方法
如果沒設置mOnTouchListener方法或者onTouch方法返回false纔會調用onTouchEvent方法
當View處於不可用狀態時,如果clickable或者long_clickable爲true,也會消耗點擊事件
if((viewFlags &ENABLED_MASK)==DISABLED){
if(event.getAction()== MotionEvent.ACTION_UP &&(mPrivateFlags &PFLAG_PRESSED)!= 0){
setPressed(false);
}
return (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONE_CLICKABLE));
}
當發生ACTION_UP時會觸發performClick方法,如果View設置了OnClickListener,那麼performClick方法內部就會調用onClick方法
view的LONG_CLICKABLE屬性默認爲false,而CLICKABLE屬性默認則由具體的控件決定,如TextView爲false
setOnClickListener 或setOnLongClickListener方法會將相應的屬性設置爲true.
參考:Android開發藝術探索