Android-事件分發(ViewGroup)

來源:互聯網
上載者:User

標籤:lis   ace   iss   包含   tar   win   流程   結構   img   

http://blog.csdn.net/guolin_blog/article/details/9153747

http://blog.csdn.net/lmj623565791/article/details/39102591

 

上一篇講了view的事件分發,這一篇主要是viewGroup

 

首先我們來探討一下,什麼是ViewGroup?它和普通的View有什麼區別?

 

顧名思義,ViewGroup就是一組View的集合,它包含很多的子View和子VewGroup,是Android中所有布局的父類或間接父類,像LinearLayout、RelativeLayout等都是繼承自ViewGroup的。但ViewGroup實際上也是一個View,只不過比起View,它多了可以包含子View和定義布局參數的功能。ViewGroup繼承結構如下所示:

 

可以看到,我們平時項目裡經常用到的各種布局,全都屬於ViewGroup的子類。

 

從Android-事件分發機制架構概述可以知道,viewgroup返回true是消費,super是判斷攔截,false是返回dispatchTouchEvent

 

源碼細節可以在這裡看:http://blog.csdn.net/lmj623565791/article/details/39102591

 

整個邏輯是這樣的:

1、ACTION_DOWN中,ViewGroup捕獲到事件,然後判斷是否攔截,如果沒有攔截,則找到包含當前x,y座標的子View,賦值給mMotionTarget,然後調用 mMotionTarget.dispatchTouchEvent

2、ACTION_MOVE中,ViewGroup捕獲到事件,然後判斷是否攔截,如果沒有攔截,則直接調用mMotionTarget.dispatchTouchEvent(ev)

3、ACTION_UP中,ViewGroup捕獲到事件,然後判斷是否攔截,如果沒有攔截,則直接調用mMotionTarget.dispatchTouchEvent(ev)

當然了在分發之前都會修改下座標系統,把當前的x,y分別減去child.left 和 child.top ,然後傳給child;

 

1、如何攔截

上面的總結都是基於:如果沒有攔截;那麼如何攔截呢?

複寫ViewGroup的onInterceptTouchEvent方法:

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中直接這麼寫:

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攔截的源碼是這樣的:

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代碼是這樣的

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);

源碼是這樣的:

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~~

源碼是這樣的

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)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.