編輯:關於Android編程
講解觸摸事件傳遞原理的網上有一大把,有從源碼角度講的,有從實際例子角度講的。我這裡呢只是記錄下自己的理解,講的可能沒其他大牛透徹,有錯誤的跪求評論指正。
直接上圖,對照圖解和文字來分析觸摸事件的傳遞。
(1)亂七八糟圖解版:
(2)看圖說話:
(2.1)上圖並不包含Activity,Activity沒有onInterceptTouchEvent方法,默認實現都是直接往下傳遞。
(2.2)講解3個觸摸事件控制方法的含義及其返回值的意義:
<1>onInterceptTouchEvent
onInterceptTouchEvent是ViewGroup特有的方法,Activity和View並沒有此方法,它的作用是用於攔截觸摸事件不再傳遞給子View(Activity和View並沒有子View,所以沒有此方法很符合邏輯),默認實現不攔截。如果攔截的話子View將完全接受不到觸摸事件,子View的任何相關觸摸事件的方法都不會被調用。(注:有例外情況我會在後面再說)(有特殊情況)
返回true:
表示攔截觸摸事件,不繼續調用子View的dispatchTouchEvent方法,直接調用自己的onTouchEvent方法。(有特殊情況)
返回false:
表示不攔截事件,繼續去調用子View的dispatchTouchEvent。
<2>dispatchTouchEvent
用於分派觸摸事件,注意是分派事件,而不是處理事件,這個方法應該做的是決定事件的傳遞方向,而不是處理消費掉觸摸事件。
返回true:
表示自己要消費這個事件,但並不是馬上消費,而是如果其子View沒人消費的話,則自己消費。事件還是會繼續往下傳遞的,只是在往上傳遞過程中如果還能到達自己的話,那麼就會去消費這個事件。
返回false:
表示自己不消費這個事件,如果同時onTouchEvent也返回false表示不消費這個事件的話,就會繼續往上傳遞給parent的onTouchEvent。
<3>onTouchEvent
用於處理事件,如果你想處理觸摸事件並做出相應的動作,那麼應該在這個方法中進行。
返回true:
表示自己想消費這個事件,並且馬上生效,即刻消費,事件傳遞停止。
返回false:
表示自己不想消費這個事件,同時檢查dispatchTouchEvent之前若是返回了true,就會發現:哎呀,我之前說子View沒人處理的話,那麼就我來處理。好吧,我要遵守諾言,就交給我處理吧,不要再傳遞了。
注意dispatchTouchEvent和onTouchEvent返回true的區別:
dispatchTouchEvent是沒人消費的話就交給我消費,你們都沒人要啊,那就給我吧,各位大爺真是好人啊。
onTouchEvent是我現在就要消費,其它人都沒份了,都干嘛干嘛去,就是這麼霸氣,就是這麼任性。
(2.3)傳遞過程中那些可能發生的事
觸摸事件的第一個接收者總是Activity,Activity一般不會去消費觸摸事件,而是直接傳遞給處於該觸摸事件范圍的View樹集合。在View樹中等級位置對應在觸摸事件中的位置,即越外層的View,就位於越頂層,而其child則位於底層。即Activity在最頂層,然後接下來是容器控件,最後的View在最底層。
在一般情況下:
事件先從上往下,經過每一個dispatchTouchEvent。到達最後一個View時,調用其onTouchEvent,開始從下往上的傳遞過程,經過每一個onTouchEvent,這是沒任何View想要消費觸摸事件的情況。
可能發生的幾個轉折點:
<1>在從上往下的過程中,如果某個View的onInterceptTouchEvent返回了true,表示攔截事件,事件不會繼續往下傳遞,而是直接調用這個View的onTouchEvent,注意這並不意味著這個View消費了這個事件,只是攔截了這個事件繼續往下傳遞,剝奪了子View的消費權,提前開始從下往上的過程而已,是否由它消費還要看其onTouchEvent的返回值是否為true。(有特殊情況)
<2>在從下往上的過程中,如果某個View的onTouchEvent返回了true,表示自己想要消費這個事件,事件不會繼續往上傳遞,事件傳遞結束。
<3>還是在從下往上的過程中,如果某個View在之前的dispatchTouchEvent方法返回了true,表示在之前這個View表明如果所有子View都不消費這個事件的話,那麼就讓我來消費,事件不會繼續往上傳遞,事件傳遞結束。
(所有子View都不消費這個事件意味著所有子View的dispatchTouchEvent和onTouchEvent都返回了false,不然事件早被消費且傳遞結束了,根本不會再到達自己。)
<4>這個比較少用,某個子View調用了parent的requestDisallowInterceptTouchEvent方法,表示請求parent不要攔截事件,這種情況下parent的onInterceptTouchEvent返回了true也攔截不了事件,即讓onInterceptTouchEvent方法失效,這就是上面3個標記了(有特殊情況)所說的特殊情況。
(2.4)記憶功能
記憶功能是指,如果某個View消費了Down事件,那麼接下來的Move和Up等事件還是會從Activity頂層向下傳遞,但是傳遞到這個View時,即使這個View並沒有用onInterceptTouchEvent方法來攔截事件,事件還是會被攔截,直接由該View處理。即如果一個View消費了Down事件,則接下來的Move和Up事件也會交給它消費。
這麼做的原因是:一般情況下單一的觸摸事件並不能形成有效的動作。比如一個拖動動作需要Down事件和連續的Move事件,點擊動作至少需要一個Down事件和Up事件。把單一的觸摸事件分派給不同的View,往往形成不了有意義的動作,所以加入了這個記憶功能來減少事件的傳遞。
(3)隨便說點
理解觸摸事件的傳遞一般是為了解決觸摸事件發生沖突的情況,處理觸摸事件沖突時要注意一點:
我們要處理的是由誰來處理觸摸事件,而不是去修改消費這個事件的View應該去怎麼處理。
有點拗口是吧,再說白一點就是我們應該控制誰來處理,而不是控制怎麼去處理,每個View都有自己的處理邏輯,我們不應該去修改它。
舉個栗子:
手指左右拖動不了ViewPager時,我們想得應該是怎麼在手指左右拖動的情況下讓ViewPager能獲得觸摸事件的消費權,而不要管接受到觸摸事件後怎麼讓ViewPager隨著手指滾動,這個功能ViewPager自己已經實現了,你把觸摸事件給它,它就能滾。
好了,本篇完結,建議對觸摸事件傳遞機制不熟悉的自己寫個例子打印些Log日志驗證一下,再百度谷歌下一下常見的觸摸沖突及其解決辦法。
osgi中bundle之間的通信,可以使用eventadmin來完成,eventadmin是osgi中的一種基於發布訂閱的方式,一個Bundle進行發布發布一個事件之後,
突然想要在android上寫一個消消樂的代碼,在此之前沒有系統地學過java的面向對象,也沒有任何android相關知識,不過還是會一點C++。8月初開始搭建環境,在這上
我們經常遇到一個需求,就是給別人使用我們工程的時候,為了能夠屏蔽代碼,把代碼封裝成jar包提供給第三方使用,但是這樣我們的資源文件怎麼給對方用呢? 網上有很多方法,有用C
shape和selector是Android UI設計中經常用到的,比如我們要自定義一個圓角Button,點擊Button有些效果的變化,就要用到shape和select