(1)在自定義view的時候常常會遇到事件攔截處理,好比在側滑菜單的時候,咱們但願在側滑菜單裏面有listview控件,可是咱們但願既能左右滑動又能上下滑動,這個時候就須要對觸摸的touch事件進行攔截。這個時候咱們就須要明白android touch 事件傳遞機制,android
(2)之前不少時候比較模糊,也許是網上看到也有不少事件傳遞的相關文章,但我看着頭暈,解釋不完全,有的說得一半,總算不知足不滿意,因而據我本身的理解來完全的來整理下具體的是怎麼個傳遞方式,以最簡單通俗易懂的方式分享給你們,但願你們看到有什麼不對的地方及時提出糾正。謝謝 ide
這是本次理解android touch 事件傳遞機制的佈局文件佈局
(1)android touch 事件傳遞機制示意圖,因爲網頁緣由會被拉伸,請你們單獨將該圖在另外一個窗口打開查看。測試
(2)事件是從Activity觸發事件而後傳遞到佈局文件,一層一層的往子容器傳遞到最底層的view,若是每層佈局文件未對該事件進行處理或者消費那麼該事件會從最底層開始往上傳到Activity進行消費。相似於一個U型。spa
(3)那麼事件的發起是由Activity界面的touch事件發起傳遞到佈局視圖,可是該視圖只是描述了佈局文件或者view的相關事件傳遞機制,Activity事件沒有進行描述,可是在下面測試中會涉及Activity相關事件傳遞來解釋心中些許疑惑日誌
android touch 事件傳遞機制示意圖能夠總結以下規律xml
(0)忘掉之前的各類說法解釋,由於各類組合說來講去的,咱們須要簡單的,換一個角度,換一個思惟,忘記事件傳遞,以最簡單通俗易懂的方式理解事件傳遞,我只須要一張圖就搞定事件傳遞機制。blog
(1)一個事件由用戶點擊觸發開始順着箭頭的方向進行傳遞,直到任意一個結束點結束事件傳遞。繼承
(2)那麼事件傳遞能夠由A傳到B,B能夠不傳到C,B不進行分發,那麼就從B再傳回A進行消費而後結束,也能夠由B傳到C而後傳遞D,或者不傳到D進行消費或者傳到父容器進行消費結束。事件
(1)事件從Activity界面開始 事件處理方法有兩個 dispatchTouchEventon 和 TouchEvent 兩種
(2)繼承ViewGroup的子類事件處理有dispatchTouchEvent、onInterceptTouchEvent 和 onTouchEvent三種
(3)繼承View的子類事件處理有dispatchTouchEventon 和 TouchEvent 兩種
(1)touch事件事件傳遞形象的理解能夠這麼認爲:好比我有一個蘋果(touch),我能夠本身吃也能夠分發(dispatchTouchEventon)給孩子吃(TouchEvent)。若是我不吃那麼我就返回給個人父親處理,若是我分發給孩子那麼孩子,那麼這個蘋果交給個人孩子他有本身獨立的權利進行處理,他能夠繼續分給他的孩子就是個人孫子進行處理,也能夠本身吃了吃掉,若是個人孫子不處理他也能夠返回給他的父親就是個人孩子處理,個人孩子也有相同的權利進行處理。
(2)接着上面其實這個事件(蘋果)的傳遞是從上往下,而後再由下往上傳遞,中途若是有人消費這個事件(吃掉蘋果),那麼這個事件就結束(蘋果沒有了),就結束傳遞。
(3)事件(蘋果)傳遞,不像咱們人同樣要害羞要矜持,推來推去,好比這個蘋果孩子不分發給他的孩子可是他本身又不想消費(吃掉)而是返回給我,那麼我就是隻有兩個選擇要麼消費(吃掉)要麼返回給個人父類進行處理,不能推來推去,就是不能孩子給我了事件(蘋果),我又來分發給孩子,這是不行的,這樣就是個死循環。
(4)總之我拿到這個事件(蘋果)會往個人孩子進行傳遞,個人孩子也能夠往他的孩子進行傳遞和消費,這樣轉往下走,若是有一個孩子消費掉這個事件(吃掉蘋果),那麼該事件結束。若是孩子都不消費那麼就會從最下面的孩子一層層傳上來傳到我手裏進行處理。
(5)記住 分發 攔截 處理。任何孩子拿到該事件第一步就是往下面分發,若是中途有攔截那麼久就本身處理,直到分到最底層就輩分最低的孩子,若是該事件就往上給父親處理。
(1)事件不管是從開始觸發仍是在傳遞到不一樣的層級佈局文件過程當中,必定會對事件進行分發,固然在父容器中若是touch事件被攔截了就不會下傳了。就是若是該事件傳遞到某一層那麼該層的dispatchTouchEventon會首先被調用進行事件分發,好比示意圖中的事件傳遞到C-ViewGroup層,那麼C-ViewGroup中的 dispatchTouchEventon這個方法是必定會被調用的進行事件分發,固然若是事件在B-ViewGroup不分發事件就會往上傳遞給父類的onTouchEvent進行處理,或者在B-ViewGroup被攔截髮事件就會往上傳遞給B-ViewGroup的onTouchEvent進行處理,也就不會傳到C-ViewGroup這層。
(2)在對事件進行分發中,dispatchTouchEventon返回值爲false表示對事件進行分發,返回true爲不分發,當事件不進行分發的時候,那麼該事件在該層就不會往下傳遞,就會返回傳遞給父容器onTouchEvent進行處理,那麼若是該事件被攔截事件就傳遞給當前容器onTouchEvent進行處理,若是當前容器onTouchEvent返回false就表示不想消費處理,那麼該事件就會往上傳遞給父容器onTouchEvent進行處理,記住onTouchEvent這裏不能往下傳,若是這裏不消費就只能往上傳遞,只有dispatchTouchEventon才能往下分發。
1、在測試驗證以前須要瞭解每一個View的子類都具備下面三個和TouchEvent處理密切相關的方法:
(1)public boolean dispatchTouchEvent(MotionEvent ev) 分發TouchEvent
(2)public boolean onInterceptTouchEvent(MotionEvent ev) 攔截TouchEvent
(3)public boolean onTouchEvent(MotionEvent ev) 處理TouchEvent
<?xml version="1.0" encoding="utf-8"?>
<boyoi.com.event.transfer.ALinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#4bf3a8">
<boyoi.com.event.transfer.BViewGroup
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="50dp"
android:background="#4a8f8f">
<boyoi.com.event.transfer.CViewGroup
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="50dp"
android:background="#847834">
<boyoi.com.event.transfer.DView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="50dp"
android:background="#765982">
</boyoi.com.event.transfer.DView>
</boyoi.com.event.transfer.CViewGroup>
</boyoi.com.event.transfer.BViewGroup>
</boyoi.com.event.transfer.ALinearLayout>
佈局文件相關代碼
/**
* Created by yishujun on 16/6/12.
*/
public class ALinearLayout extends LinearLayout{
private final String TAG = "ALinearLayout";
public ALinearLayout(Context context) {
super(context);
}
public ALinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ALinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.i(TAG,"dispatchTouchEvent" );
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.i(TAG,"onInterceptTouchEvent" );
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG,"onTouchEvent" );
return super.onTouchEvent(event);
}
}
/**
* Created by yishujun on 16/6/12.
*/
public class BViewGroup extends ViewGroup{
private final String TAG = "BViewGroup";
public BViewGroup(Context context) {
super(context);
}
public BViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
}
public BViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//該demo主要講解android的事件傳遞,這裏不解釋,若有疑問請關注我接下來的自定義view相關博客
measureChildren(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//該demo主要講解android的事件傳遞,這裏不解釋,,若有疑問請關注我接下來的自定義view相關博客
//下面爲何這麼處理,由於例子中只有一共孩子,getChildAt(0)只能是隻有一個孩子,否則得不到咱們要的效果
View childView = getChildAt(0);
childView.layout(l, t, r-2*l, b-2*t);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.i(TAG,"dispatchTouchEvent" );
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.i(TAG,"onInterceptTouchEvent" );
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG,"onTouchEvent" );
return super.onTouchEvent(event);
}
}
/**
* Created by yishujun on 16/6/12.
*/
public class CViewGroup extends ViewGroup {
private final String TAG = "CViewGroup";
public CViewGroup(Context context) {
super(context);
}
public CViewGroup(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CViewGroup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//該demo主要講解android的事件傳遞,這裏不解釋,若有疑問請關注我接下來的自定義view相關博客
measureChildren(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
//該demo主要講解android的事件傳遞,這裏不解釋,,若有疑問請關注我接下來的自定義view相關博客
//下面爲何這麼處理,由於例子中只有一共孩子,getChildAt(0)只能是隻有一個孩子,否則得不到咱們要的效果
View childView = getChildAt(0);
childView.layout(l, t, r-2*l, b-2*t);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.i(TAG,"dispatchTouchEvent" );
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.i(TAG,"onInterceptTouchEvent" );
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG,"onTouchEvent" );
return super.onTouchEvent(event);
}
}
/**
* Created by yishujun on 16/6/12.
*/
public class DView extends View{
private final String TAG = "DView";
public DView(Context context) {
super(context);
}
public DView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public DView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
// 計算出本身的寬高
setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.i(TAG,"dispatchTouchEvent" );
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG,"onTouchEvent" );
return super.onTouchEvent(event);
}
}
public class MainActivity extends AppCompatActivity {
private final String TAG = "MainActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.i(TAG,"dispatchTouchEvent" );
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG,"onTouchEvent" );
return super.onTouchEvent(event);
}
}
測試用例一:因此的 touch事件都分發不攔截不消費,全部的dispatchTouchEvent,onInterceptTouchEvent,onTouchEven方法t都返回false,如以上代碼默認代碼,點擊中間的D-View:
06-12 11:09:56.274 31712-31712/boyoi.com.event.transfer I/MainActivity: dispatchTouchEvent
06-12 11:09:56.274 31712-31712/boyoi.com.event.transfer I/ALinearLayout: dispatchTouchEvent
06-12 11:09:56.274 31712-31712/boyoi.com.event.transfer I/ALinearLayout: onInterceptTouchEvent
06-12 11:09:56.274 31712-31712/boyoi.com.event.transfer I/BViewGroup: dispatchTouchEvent
06-12 11:09:56.274 31712-31712/boyoi.com.event.transfer I/BViewGroup: onInterceptTouchEvent
06-12 11:09:56.274 31712-31712/boyoi.com.event.transfer I/CViewGroup: dispatchTouchEvent
06-12 11:09:56.274 31712-31712/boyoi.com.event.transfer I/CViewGroup: onInterceptTouchEvent
06-12 11:09:56.274 31712-31712/boyoi.com.event.transfer I/DView: dispatchTouchEvent
06-12 11:09:56.274 31712-31712/boyoi.com.event.transfer I/DView: onTouchEvent
06-12 11:09:56.274 31712-31712/boyoi.com.event.transfer I/CViewGroup: onTouchEvent
06-12 11:09:56.274 31712-31712/boyoi.com.event.transfer I/BViewGroup: onTouchEvent
06-12 11:09:56.274 31712-31712/boyoi.com.event.transfer I/ALinearLayout: onTouchEvent
06-12 11:09:56.274 31712-31712/boyoi.com.event.transfer I/MainActivity: onTouchEvent
06-12 11:09:56.377 31712-31712/boyoi.com.event.transfer I/MainActivity: dispatchTouchEvent
06-12 11:09:56.377 31712-31712/boyoi.com.event.transfer I/MainActivity: onTouchEvent
測試用例二:在mainActivity界面對touch事件不進行分發,mainActivity 裏面的dispatchTouchEvent返回true 則結果:
06-12 11:26:24.288 31712-31712/boyoi.com.event.transfer I/MainActivity: dispatchTouchEvent
06-12 11:26:24.390 31712-31712/boyoi.com.event.transfer I/MainActivity: dispatchTouchEvent
由以上日誌可知,若是Activity界面不進行事件分發那麼事件將沒法往下傳遞
測試用例三:在mainActivity界面對touch事件進行分發,可是在mainActivity 對該事件進行消費,不往下面傳遞,mainActivity 裏面的 dispatchTouchEvent返回false,onTouchEvent 返回true 則結果:
06-12 11:32:06.521 31712-31712/boyoi.com.event.transfer I/MainActivity: dispatchTouchEvent
06-12 11:32:06.521 31712-31712/boyoi.com.event.transfer I/ALinearLayout: dispatchTouchEvent
06-12 11:32:06.521 31712-31712/boyoi.com.event.transfer I/ALinearLayout: onInterceptTouchEvent
06-12 11:32:06.521 31712-31712/boyoi.com.event.transfer I/BViewGroup: dispatchTouchEvent
06-12 11:32:06.521 31712-31712/boyoi.com.event.transfer I/BViewGroup: onInterceptTouchEvent
06-12 11:32:06.521 31712-31712/boyoi.com.event.transfer I/CViewGroup: dispatchTouchEvent
06-12 11:32:06.521 31712-31712/boyoi.com.event.transfer I/CViewGroup: onInterceptTouchEvent
06-12 11:32:06.521 31712-31712/boyoi.com.event.transfer I/DView: dispatchTouchEvent
06-12 11:32:06.521 31712-31712/boyoi.com.event.transfer I/DView: onTouchEvent
06-12 11:32:06.521 31712-31712/boyoi.com.event.transfer I/CViewGroup: onTouchEvent
06-12 11:32:06.521 31712-31712/boyoi.com.event.transfer I/BViewGroup: onTouchEvent
06-12 11:32:06.522 31712-31712/boyoi.com.event.transfer I/ALinearLayout: onTouchEvent
06-12 11:32:06.522 31712-31712/boyoi.com.event.transfer I/MainActivity: onTouchEvent
06-12 11:32:06.634 31712-31712/boyoi.com.event.transfer I/MainActivity: dispatchTouchEvent
06-12 11:32:06.634 31712-31712/boyoi.com.event.transfer I/MainActivity: onTouchEvent
結果與測試用例同樣的效果
測試用例四:在CViewGroup界面對touch事件進行攔截, 並本身消費該事件,CViewGroup 裏面的 onInterceptTouchEvent返回true,onTouchEvent 返回true 則結果:
06-12 11:38:28.949 31712-31712/boyoi.com.event.transfer I/MainActivity: dispatchTouchEvent
06-12 11:38:28.949 31712-31712/boyoi.com.event.transfer I/ALinearLayout: dispatchTouchEvent
06-12 11:38:28.949 31712-31712/boyoi.com.event.transfer I/ALinearLayout: onInterceptTouchEvent
06-12 11:38:28.949 31712-31712/boyoi.com.event.transfer I/BViewGroup: dispatchTouchEvent
06-12 11:38:28.949 31712-31712/boyoi.com.event.transfer I/BViewGroup: onInterceptTouchEvent
06-12 11:38:28.949 31712-31712/boyoi.com.event.transfer I/CViewGroup: dispatchTouchEvent
06-12 11:38:28.949 31712-31712/boyoi.com.event.transfer I/CViewGroup: onTouchEvent
由以上日誌能夠看到,事件傳遞到CViewGroup就不在往下傳遞了,也不往上傳遞了,本身消費了
測試用例五:在CViewGroup界面對touch事件進行攔截, 本身不消費該事件,CViewGroup 裏面的 onInterceptTouchEvent返回true,onTouchEvent 返回false 則結果:
06-12 11:41:23.857 31712-31712/boyoi.com.event.transfer I/MainActivity: dispatchTouchEvent
06-12 11:41:23.857 31712-31712/boyoi.com.event.transfer I/ALinearLayout: dispatchTouchEvent
06-12 11:41:23.857 31712-31712/boyoi.com.event.transfer I/ALinearLayout: onInterceptTouchEvent
06-12 11:41:23.857 31712-31712/boyoi.com.event.transfer I/BViewGroup: dispatchTouchEvent
06-12 11:41:23.857 31712-31712/boyoi.com.event.transfer I/BViewGroup: onInterceptTouchEvent
06-12 11:41:23.857 31712-31712/boyoi.com.event.transfer I/CViewGroup: dispatchTouchEvent
06-12 11:41:23.857 31712-31712/boyoi.com.event.transfer I/CViewGroup: onInterceptTouchEvent
06-12 11:41:23.857 31712-31712/boyoi.com.event.transfer I/CViewGroup: onTouchEvent
06-12 11:41:23.857 31712-31712/boyoi.com.event.transfer I/BViewGroup: onTouchEvent
06-12 11:41:23.857 31712-31712/boyoi.com.event.transfer I/ALinearLayout: onTouchEvent
06-12 11:41:23.857 31712-31712/boyoi.com.event.transfer I/MainActivity: onTouchEvent
06-12 11:41:23.936 31712-31712/boyoi.com.event.transfer I/MainActivity: dispatchTouchEvent
06-12 11:41:23.936 31712-31712/boyoi.com.event.transfer I/MainActivity: onTouchEvent
由以上日誌能夠看到,事件傳遞到CViewGroup就不在往下傳遞了,可是本身也並未消費,而是將該事件傳遞給父容器了
好了其餘的測試例子本身能夠進行嘗試,我就不一一嘗試了,若是有疑問或者錯誤須要糾正請留言,我通常看到會及時回答,謝謝交流溝通。後面接下來我還會寫一些自定義控件來闡述事件傳遞在何時用和怎麼用,但願支持鼓勵。