事件分發(fā)機(jī)制從ViewGroup的dispatchTouchEvent入手
public boolean dispatchTouchEvent(MotionEvent ev) {
...
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); // restore action in case it was changed
}
} else {
intercepted = true;
}
if (!canceled && !intercepted) {
...
TouchTarget predecessor = null;
TouchTarget target = mFirstTouchTarget;
while (target != null) {
final TouchTarget next = target.next;
if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
handled = true;
} else {
final boolean cancelChild = resetCancelNextUpFlag(target.child)
|| intercepted;
if (dispatchTransformedTouchEvent(ev, cancelChild,
target.child, target.pointerIdBits)) {
handled = true;
}
}
...
}
}
}
可以看出承二,ViewGroup的dispatchTouchEvent,首先調(diào)用了自己的onInterceptTouchEvent方法侣诵,如果此方法返回true树绩,則會(huì)短路后面的邏輯峦筒。進(jìn)入下列邏輯
if (mFirstTouchTarget == null) {
// No touch targets so treat this as an ordinary view.
handled = dispatchTransformedTouchEvent(ev, canceled, null,
TouchTarget.ALL_POINTER_IDS);
}
然后調(diào)用
super.dispatchTouchEvent(event);
即View的方法氏义,然后調(diào)用ViewGroup的onTouchEvent(event)方法
如果ViewGroup的onInterceptTouchEvent返回false則會(huì)調(diào)用dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)锄列,最終調(diào)用 handled = child.dispatchTouchEvent(event);到這里又是一個(gè)循環(huán)了
用圖表示
那么,回到滑動(dòng)沖突惯悠,就簡(jiǎn)單了邻邮。
外部攔截法
需要外部控件重寫(xiě)父類的onInterceptTouchEvent 方法,在其中判斷什么時(shí)候需要攔截事件由自身處理克婶,什么時(shí)候需要放行將事件傳給內(nèi)層控件處理筒严,內(nèi)部控件不需要做任何處理。偽代碼如下:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
if (父容器需要自己處理改事件) {
return true;//攔截
} else {
return false;//不攔截
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
break;
}
return super.onInterceptTouchEvent(ev);
}
內(nèi)部攔截法
這種方式需要結(jié)合 requestDisllowInterceptTouchEvent(boolean) 這個(gè)方法鸠补,外部不攔截的情況下萝风,會(huì)調(diào)用此方法嘀掸,在內(nèi)層控件的重寫(xiě)方法dispatchTouchEvent中紫岩,根據(jù)邏輯來(lái)決定外層控件何時(shí)需要攔截事件,何時(shí)需要放行睬塌。(requestDisllowInterceptTouchEvent 這個(gè)方法的作用是-是否允許外層控件攔截事件)偽代碼如下:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN:
getParent().requestDisallowInterceptTouchEvent(true);
break;
case MotionEvent.ACTION_MOVE:
if (父容器需要處理改事件) {
//允許外層控件攔截事件
getParent().requestDisallowInterceptTouchEvent(false);
} else {
//需要內(nèi)部控件處理該事件泉蝌,不允許上層viewGroup攔截
getParent().requestDisallowInterceptTouchEvent(true);
}
break;
case MotionEvent.ACTION_UP:
break;
default:
break;
}
return super.dispatchTouchEvent(ev);
}
同時(shí),在這種方法下需要在外層控件做下處理:
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
return false;
} else {
return true;
}
}
注:調(diào)用 getParent().requestDisallowInterceptTouchEvent(true)方法之后揩晴,父控件在dispatchTouchEvent的時(shí)候會(huì)直接將事件傳遞到子控件中