Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android Touch事件傳遞機制剖析

Android Touch事件傳遞機制剖析

編輯:關於Android編程

 

// 表示事件是否攔截, 返回false表示不攔截
	@Override
	public boolean onInterceptTouchEvent(MotionEvent arg0) {
		return false;
	}

	/**
	 * 重寫onTouchEvent事件,什麼都不用做
	 */
	@Override
	public boolean onTouchEvent(MotionEvent arg0) {
		return false;
	}


 

 

/**
	 * 事件分發, 請求父控件及祖宗控件是否攔截事件
	 */
	@Override
	public boolean dispatchTouchEvent(MotionEvent ev) {
		if (getCurrentItem() != 0) {
			getParent().requestDisallowInterceptTouchEvent(true);// 用getParent去請求,
																	// 不攔截
		} else {// 如果是第一個頁面,需要顯示側邊欄, 請求父控件攔截
			getParent().requestDisallowInterceptTouchEvent(false);// 攔截
		}
		return super.dispatchTouchEvent(ev);
	}


 

開篇語:最近程序在做一個小效果,要用到touch,結果整得雲裡面霧裡的,干脆就好好把android touch機制好好看了一下,呵呵。。

android系統中的每個ViewGroup的子類都具有下面三個和TouchEvent處理密切相關的方法:

1)public boolean dispatchTouchEvent(MotionEvent ev) 這個方法用來分發TouchEvent

2)public boolean onInterceptTouchEvent(MotionEvent ev) 這個方法用來攔截TouchEvent

3)public boolean onTouchEvent(MotionEvent ev) 這個方法用來處理TouchEvent

注意:不是所有的View的子類,很多教程都說的是所有的View的子類,只有可以向裡面添加View的控件才需要分發,比如TextView它本身就是最小的view了,所以不用再向它的子視圖分發了,它也沒有子視圖了,所以它沒有dispatch和Intercept,只有touchEvent。

 

device-2012-03-24-084959.png

說明: 白色為最外層,它占滿整個屏幕;

紅色為中間區域,屬於白色中的一層;

黑色為中心區域,必於紅色中的一層。

注意:他們本質上是:LinearLayout,而不是RelativeLayout或者其它布局。

1.由中心區域處理touch事件

布局文件如下:

  1.  
  2. android:layout_width="fill_parent"
  3. android:layout_height="fill_parent"
  4. android:orientation="vertical">
  5. android:id="@+id/view_out"
  6. android:layout_width="fill_parent"
  7. android:layout_height="fill_parent"
  8. android:background="#fff"
  9. android:gravity="center">
  10. android:id="@+id/view_mid"
  11. android:layout_width="300px"
  12. android:layout_height="400px"
  13. android:background="#f00"
  14. android:gravity="center">
  15. android:id="@+id/view_center"
  16. android:layout_width="150px"
  17. android:layout_height="150px"
  18. android:background="#000"
  19. android:gravity="center"
  20. android:clickable="true">
  21.  
  22.  
  23.  
  24.  
  25. 復制代碼

    注意: android:clickable="true"

    接下來我們看一下打印的日志:

    1111.png

    結合是上面的日志,我們可以看一下ACTION_DOWN事件處理流程:

    a0dfaa98gb7f95585f7a2&690.png

    說明:

    首先觸摸事件發生時(ACTION_DOWN),由系統調用Activity的dispatchTouchEvent方法,分發該事件。根據觸摸事件的坐標,將此事件傳遞給out的dispatchTouchEvent處理,out則調用onInterceptTouchEvent 判斷事件是由自己處理,還是繼續分發給子View。此處由於out不處理Touch事件,故根據事件發生坐標,將事件傳遞給out的直接子View(即middle)。

    Middle及Center中事件處理過程同上。但是由於Center組件是clickable 表示其能處理Touch事件,故center中的onInterceptTouchEvent方法將事件傳遞給center自己的onTouchEvent方法處理。至此,此Touch事件已被處理,不繼續進行傳遞。

    2.沒有指定誰會處理touch事件

    布局文件如下:

    1.  
    2. android:layout_width="fill_parent"
    3. android:layout_height="fill_parent"
    4. android:orientation="vertical">
    5. android:id="@+id/view_out"
    6. android:layout_width="fill_parent"
    7. android:layout_height="fill_parent"
    8. android:background="#fff"
    9. android:gravity="center">
    10. android:id="@+id/view_mid"
    11. android:layout_width="300px"
    12. android:layout_height="400px"
    13. android:background="#f00"
    14. android:gravity="center">
    15. android:id="@+id/view_center"
    16. android:layout_width="150px"
    17. android:layout_height="150px"
    18. android:background="#000"
    19. android:gravity="center">
    20.  
    21.  
    22.  
    23.  
    24. 復制代碼

      注意:只是比上一次的布局少了android:clickable="true"
      接下來我們看一下打印的日志

      2222.png

      結合是上面的日志,我們可以看一下ACTION_DOWN事件處理流程:

      a0dfaa98gb7f9559d8155&690.png

      說明:

      事件處理流程大致同上,區別是此狀態下,所有組件都不會處理事件,事件並不會被center的onTouchEvent方法“消費”,則事件會層層逆向傳遞回到Activity,若Activity也不對此事件進行處理,此事件相當於消失了(無效果)。

      對於後續的move、up事件,由於第一個down事件已經確定由Activity處理事件,故up事有由Activity的dispatchTouchEvent直接分發給自己的onTouchEvent方法處理。

      代碼請看最後的附件

      總結:

      1) Touchevent 中,返回值是 true ,則說明消耗掉了這個事件,返回值是 false ,則沒有消耗掉,會繼續傳遞下去,這個是最基本的。2) 事件傳遞的兩種方式:
      隧道方式:從根元素依次往下傳遞直到最內層子元素或在中間某一元素中由於某一條件停止傳遞。
      冒泡方式:從最內層子元素依次往外傳遞直到根元素或在中間某一元素中由於某一條件停止傳遞。 android對Touch Event的分發邏輯是View從上層分發到下層(dispatchTouchEvent函數)類似於隧道方式,然後下層優先開始處理Event(先mOnTouchListener,再onTouchEvent)並向上返回處理情況(boolean值),若返回true,則上層不再處理。類似於冒泡方式
      於是難題出現了,你若把Touch Event都想辦法給傳到上層了(只能通過返回false來傳到上層),那麼下層的各種子View就不能處理後續事件了。而有的時候我們需要在下層和上層都處理Touch事件

      舉個例子,ViewFlipper用來檢測手勢,在內部我們放幾個Image,有點像gallery的效果,也就是左右滑動切換圖片,但是圖片有時候我們希望可以放大縮小!這樣就會存在ViewFlipper裡面需要touch事件,而在image裡面也需要一個touch事件(當圖片大小屏幕邊界的時候可以拖動圖片,而不是左右切換圖片)。

      我首先的思路是著手於事件回傳的方式,研究了n久,實際了n久,都沒達到自己想要的結果 ,我甚至於把gallery和gallery3D 的源碼下載下來看了N久也沒辦法去解決,在這裡隨便說一下gallery吧,gallery雖然在這個效果,但是人家並不是ViewFlipper加image這樣來實現的,人家是像游戲這樣用一個view來統一處理的,我們可以簡單的理解成自定義了一個控件,這樣touch事件想怎麼處理就怎麼處理,不過就是邏輯復雜了,我們想偷懶就沒辦法了,呵呵。。。

      最後不停的試啊試啊,想到一個可行的方案,但是我覺得不是很靠譜,也就是:我們在ViewFlipper這裡,我們先把所有的touch都截取到,然後在他的onTouchEvent中,我們先調用imageview的onTouchEvent事件,如果返回true,證明這個事件,imageview要用,那麼ViewFlipper就當什麼事都沒發生,如果imageview返回的false,則調用自己的touchEvent.偽代碼如下:

      1. //自定義一個MyViewFlipper 繼承於ViewFlipper,並且實現onTouchEvent方式,

        復制代碼

        我覺得他不靠譜的原因為: 1. 他打斷了android的原有的機制,不是很提倡。
        2. 得試先知道ViewFlipper裡面的控件,或者說通過某種路徑能獲取到
        3. 如果ViewFlipper裡面的控件多了,就蛋疼了

        好了,如果有什麼意見或者建議,大家一起討論。

         
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved