標籤:
3、關於攔截1、如何攔截
上面的總結都是基於:如果沒有攔截;那麼如何攔截呢?
複寫ViewGroup的onInterceptTouchEvent方法:
[java] view plain copy
- @Override
- public boolean onInterceptTouchEvent(MotionEvent ev)
- {
- int action = ev.getAction();
- switch (action)
- {
- case MotionEvent.ACTION_DOWN:
- //如果你覺得需要攔截
- return true ;
- case MotionEvent.ACTION_MOVE:
- //如果你覺得需要攔截
- return true ;
- case MotionEvent.ACTION_UP:
- //如果你覺得需要攔截
- return true ;
- }
-
- return false;
- }
預設是不攔截的,即返回false;如果你需要攔截,只要return true就行了,這要該事件就不會往子View傳遞了,並且如果你在DOWN retrun true ,則DOWN,MOVE,UP子View都不會捕獲事件;如果你在MOVE return true , 則子View在MOVE和UP都不會捕獲事件。
原因很簡單,當onInterceptTouchEvent(ev) return true的時候,會把mMotionTarget 置為null ;
2、如何不被攔截
如果ViewGroup的onInterceptTouchEvent(ev) 當ACTION_MOVE時return true ,即攔截了子View的MOVE以及UP事件;
此時子View希望依然能夠響應MOVE和UP時該咋辦呢?
Android給我們提供了一個方法:requestDisallowInterceptTouchEvent(boolean) 用於設定是否允許攔截,我們在子View的dispatchTouchEvent中直接這麼寫:
[java] view plain copy
- @Override
- public boolean dispatchTouchEvent(MotionEvent event)
- {
- getParent().requestDisallowInterceptTouchEvent(true);
- int action = event.getAction();
-
- switch (action)
- {
- case MotionEvent.ACTION_DOWN:
- Log.e(TAG, "dispatchTouchEvent ACTION_DOWN");
- break;
- case MotionEvent.ACTION_MOVE:
- Log.e(TAG, "dispatchTouchEvent ACTION_MOVE");
- break;
- case MotionEvent.ACTION_UP:
- Log.e(TAG, "dispatchTouchEvent ACTION_UP");
- break;
-
- default:
- break;
- }
- return super.dispatchTouchEvent(event);
- }
getParent().requestDisallowInterceptTouchEvent(true); 這樣即使ViewGroup在MOVE的時候return true,子View依然可以捕獲到MOVE以及UP事件。
從源碼也可以解釋:
ViewGroup MOVE和UP攔截的源碼是這樣的:
[java] view plain copy
- if (!disallowIntercept && onInterceptTouchEvent(ev)) {
- final float xc = scrolledXFloat - (float) target.mLeft;
- final float yc = scrolledYFloat - (float) target.mTop;
- mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
- ev.setAction(MotionEvent.ACTION_CANCEL);
- ev.setLocation(xc, yc);
- if (!target.dispatchTouchEvent(ev)) {
- // target didn‘t handle ACTION_CANCEL. not much we can do
- // but they should have.
- }
- // clear the target
- mMotionTarget = null;
- // Don‘t dispatch this event to our own view, because we already
- // saw it when intercepting; we just want to give the following
- // event to the normal onTouchEvent().
- return true;
- }
當我們把disallowIntercept設定為true時,!disallowIntercept直接為false,於是攔截的方法體就被跳過了~
註:如果ViewGroup在onInterceptTouchEvent(ev) ACTION_DOWN裡面直接return true了,那麼子View是木有辦法的捕獲事件的~~~
4、如果沒有找到合適的子View
我們的執行個體,直接點擊ViewGroup內的按鈕,當然直接很順利的走完整個流程;
但是有兩種特殊情況
1、ACTION_DOWN的時候,子View.dispatchTouchEvent(ev)返回的為false ;
如果你仔細看了,你會注意到ViewGroup的dispatchTouchEvent(ev)的ACTION_DOWN代碼是這樣的
[java] view plain copy
- if (child.dispatchTouchEvent(ev)) {
- // Event handled, we have a target now.
- mMotionTarget = child;
- return true;
- }
只有在child.dispatchTouchEvent(ev)返回true了,才會認為找到了能夠處理當前事件的View,即mMotionTarget = child;
但是如果返回false,那麼mMotionTarget 依然是null
mMotionTarget 為null會咋樣呢?
其實ViewGroup也是View的子類,如果沒有找到能夠處理該事件的子View,或者乾脆就沒有子View;
那麼,它作為一個View,就相當於View的事件轉寄了~~直接super.dispatchTouchEvent(ev);
源碼是這樣的:
[java] view plain copy
- final View target = mMotionTarget;
- if (target == null) {
- // We don‘t have a target, this means we‘re handling the
- // event as a regular view.
- ev.setLocation(xf, yf);
- if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
- ev.setAction(MotionEvent.ACTION_CANCEL);
- mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
- }
- return super.dispatchTouchEvent(ev);
- }
我們沒有一個能夠處理該事件的目標元素,意味著我們需要自己處理~~~就相當於傳統的View~
2、那麼什麼時候子View.dispatchTouchEvent(ev)返回的為true
如果你仔細看了上篇部落格,你會發現只要子View支援點擊或者長按事件一定返回true~~
源碼是這樣的:
[java] view plain copy
-
- if (((viewFlags & CLICKABLE) == CLICKABLE ||
- (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
- return true ; }
5、總結
關於代碼流程上面已經總結過了~
1、如果ViewGroup找到了能夠處理該事件的View,則直接交給子View處理,自己的onTouchEvent不會被觸發;
2、可以通過複寫onInterceptTouchEvent(ev)方法,攔截子View的事件(即return true),把事件交給自己處理,則會執行自己對應的onTouchEvent方法
3、子View可以通過調用getParent().requestDisallowInterceptTouchEvent(true); 阻止ViewGroup對其MOVE或者UP事件進行攔截;
好了,那麼實際應用中能解決哪些問題呢?
比如你需要寫一個類似slidingmenu的左側隱藏menu,主Activity上有個Button、ListView或者任何可以響應點擊的View,你在當前View上死命的滑動,功能表列也出不來;因為MOVE事件被子View處理了~ 你需要這麼做:在ViewGroup的dispatchTouchEvent中判斷使用者是不是想顯示菜單,如果是,則在onInterceptTouchEvent(ev)攔截子View的事件;自己進行處理,這樣自己的onTouchEvent就可以順利展現出菜單
(轉)Android ViewGroup事件分發機制