一、基本知識
1.事件分發(fā)的三個函數(shù)
事件的分發(fā) dispatchTouchEvent()
事件的攔截 onInterceptTouchEvent()
事件的處理(消費) onTouchEvent()
2.事件分發(fā)的對象
被分發(fā)的對象是那些?被分發(fā)的對象是用戶觸摸屏幕而產(chǎn)生的點擊事件,事件主要包括:按下、滑動、抬起與取消。這些事件被封裝成MotionEvent對象。
MotionEvent.ACTION_DOWN 在屏幕按下時 MotionEvent.ACTION_MOVE 在屏幕上滑動時 MotionEvent.ACTION_UP 在屏幕抬起時 MotionEvent.ACTION_CANCLE 滑動超出控件邊界時
3.分發(fā)事件的組件
分發(fā)事件的組件,也稱為分發(fā)事件者,包括Activity、View和ViewGroup。它們?nèi)叩囊话憬Y(jié)構(gòu)為:
二、事件處理流程
首先,我們需要了解事件處理中的幾個方法:
1、在ViewGroup中,事件分為dispatchTouchEvent(事件的分發(fā)),onInterceptTouchEvent(事件的攔截),onTouchEvent(事件的處理)。
2、在View中,事件分為dispatchTouchEvent(事件的分發(fā)),onTouchEvent(事件的處理)。
下面是demo的界面結(jié)構(gòu),它是由兩個自定義的ViewGroup和一個自定義的View組成,并分別重寫了它們的以上幾個方法。
其中MyViewGroupA代碼如下:
public class MyViewGroupA extends LinearLayout { public MyViewGroupA(Context context) { super(context); } public MyViewGroupA(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()){ case MotionEvent.ACTION_DOWN: Log.i(“MyViewGroupA”,”dispatchTouchEvent_ACTION_DOWN”); break; case MotionEvent.ACTION_MOVE: Log.i(“MyViewGroupA”,”dispatchTouchEvent_ACTION_MOVE”); break; case MotionEvent.ACTION_UP: Log.i(“MyViewGroupA”,”dispatchTouchEvent_ACTION_UP”); break; } return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()){ case MotionEvent.ACTION_DOWN: Log.i(“MyViewGroupA”,”onInterceptTouchEvent_ACTION_DOWN”); break; case MotionEvent.ACTION_MOVE: Log.i(“MyViewGroupA”,”onInterceptTouchEvent_ACTION_MOVE”); break; case MotionEvent.ACTION_UP: Log.i(“MyViewGroupA”,”onInterceptTouchEvent_ACTION_UP”); break; } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: Log.i(“MyViewGroupA”,”onTouchEvent_ACTION_DOWN”); break; case MotionEvent.ACTION_MOVE: Log.i(“MyViewGroupA”,”onTouchEvent_ACTION_MOVE”); break; case MotionEvent.ACTION_UP: Log.i(“MyViewGroupA”,”onTouchEvent_ACTION_UP”); break; } return super.onTouchEvent(event); }}
MyViewGroupB代碼如下:
public class MyViewGroupB extends LinearLayout { public MyViewGroupB(Context context) { super(context); } public MyViewGroupB(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()){ case MotionEvent.ACTION_DOWN: Log.i(“MyViewGroupB”,”dispatchTouchEvent_ACTION_DOWN”); break; case MotionEvent.ACTION_MOVE: Log.i(“MyViewGroupB”,”dispatchTouchEvent_ACTION_MOVE”); break; case MotionEvent.ACTION_UP: Log.i(“MyViewGroupB”,”dispatchTouchEvent_ACTION_UP”); break; } return super.dispatchTouchEvent(ev); } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()){ case MotionEvent.ACTION_DOWN: Log.i(“MyViewGroupB”,”onInterceptTouchEvent_ACTION_DOWN”); break; case MotionEvent.ACTION_MOVE: Log.i(“MyViewGroupB”,”onInterceptTouchEvent_ACTION_MOVE”); break; case MotionEvent.ACTION_UP: Log.i(“MyViewGroupB”,”onInterceptTouchEvent_ACTION_UP”); break; } return super.onInterceptTouchEvent(ev); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: Log.i(“MyViewGroupB”,”onTouchEvent_ACTION_DOWN”); break; case MotionEvent.ACTION_MOVE: Log.i(“MyViewGroupB”,”onTouchEvent_ACTION_MOVE”); break; case MotionEvent.ACTION_UP: Log.i(“MyViewGroupB”,”onTouchEvent_ACTION_UP”); break; } return super.onTouchEvent(event); }}
MyView代碼如下:
public class MyView extends View { public MyView(Context context) { super(context); } public MyView(Context context, AttributeSet attrs) { super(context, attrs); } @Override public boolean dispatchTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: Log.i(“MyView”,”dispatchTouchEvent_ACTION_DOWN”); break; case MotionEvent.ACTION_MOVE: Log.i(“MyView”,”dispatchTouchEvent_ACTION_MOVE”); break; case MotionEvent.ACTION_UP: Log.i(“MyView”,”dispatchTouchEvent_ACTION_UP”); break; } return super.dispatchTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: Log.i(“MyView”,”onTouchEvent_ACTION_DOWN”); break; case MotionEvent.ACTION_MOVE: Log.i(“MyView”,”onTouchEvent_ACTION_MOVE”); break; case MotionEvent.ACTION_UP: Log.i(“MyView”,”onTouchEvent_ACTION_UP”); break; } return super.onTouchEvent(event); }}
我們說過,事件傳遞是由上到下的,所以最外層的View首先對事件進行操作。而我們最外層是Activity,所以事件也是從這里開始。 Activity代碼如下:
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } @Override public boolean dispatchTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: Log.i(“Activity”,”dispatchTouchEvent_ACTION_DOWN”); break; case MotionEvent.ACTION_MOVE: Log.i(“Activity”,”dispatchTouchEvent_ACTION_MOVE”); break; case MotionEvent.ACTION_UP: Log.i(“Activity”,”dispatchTouchEvent_ACTION_UP”); break; } return super.dispatchTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: Log.i(“Activity”,”onTouchEvent_ACTION_DOWN”); break; case MotionEvent.ACTION_MOVE: Log.i(“Activity”,”onTouchEvent_ACTION_MOVE”); break; case MotionEvent.ACTION_UP: Log.i(“Activity”,”onTouchEvent_ACTION_UP”); break; } return super.onTouchEvent(event); }}
現(xiàn)在我們通過觸摸MyView開始進行分析。雖然dispatchTouchEvent是事件開始的第一步,但是在開發(fā)中,我們通常很少改寫它,所以我們下面只討論其他兩個方法。 1、對以上方法均不作處理,都返回super。這意味著我們既不攔截,也不消費。
大家看輸出結(jié)果:
I/Activity: dispatchTouchEvent_ACTION_DOWNI/MyViewGroupA: dispatchTouchEvent_ACTION_DOWNI/MyViewGroupA: onInterceptTouchEvent_ACTION_DOWNI/MyViewGroupB: dispatchTouchEvent_ACTION_DOWNI/MyViewGroupB: onInterceptTouchEvent_ACTION_DOWNI/MyView: dispatchTouchEvent_ACTION_DOWNI/MyView: onTouchEvent_ACTION_DOWNI/MyViewGroupB: onTouchEvent_ACTION_DOWNI/MyViewGroupA: onTouchEvent_ACTION_DOWNI/Activity: onTouchEvent_ACTION_DOWNI/Activity: dispatchTouchEvent_ACTION_MOVEI/Activity: onTouchEvent_ACTION_MOVEI/Activity: dispatchTouchEvent_ACTION_UPI/Activity: onTouchEvent_ACTION_UP
結(jié)合輸出結(jié)果,我們可以總結(jié)出以下的結(jié)論:
結(jié)合流程圖,不難發(fā)現(xiàn),如果我對事件既不攔截,也不消費,當觸發(fā)ACTION_DOWN的時候,事件會經(jīng)過Activity——MyViewGroupA——MyViewGroupB——MyView一層層的向下進行dispatchTouchEvent(分發(fā))—onInterceptTouchEvent(攔截)調(diào)用。當?shù)竭_最底層MyView后,開始觸發(fā)消費操作,因為我均不消費,ACTION_DOWN將由底層一層層向上冒,移交上層處理。當?shù)诌_最上層Activity后,說明下層均不消費,之后觸發(fā)的ACTION_MOVE和ACTION_UP將不再向下層分發(fā)傳遞,直接交由Activity分發(fā)給自己進行處理。
2、我們將MyVIewGroupB的onInterceptTouchEvent返回值改為true,其他均是super。這意味著僅僅MyViewGroupB進行事件攔截,但均無消費
輸出結(jié)果如下:
I/Activity: dispatchTouchEvent_ACTION_DOWNI/MyViewGroupA: dispatchTouchEvent_ACTION_DOWNI/MyViewGroupA: onInterceptTouchEvent_ACTION_DOWNI/MyViewGroupB: dispatchTouchEvent_ACTION_DOWNI/MyViewGroupB: onInterceptTouchEvent_ACTION_DOWNI/MyViewGroupB: onTouchEvent_ACTION_DOWNI/MyViewGroupA: onTouchEvent_ACTION_DOWNI/Activity: onTouchEvent_ACTION_DOWNI/Activity: dispatchTouchEvent_ACTION_MOVEI/Activity: onTouchEvent_ACTION_MOVEI/Activity: dispatchTouchEvent_ACTION_UPI/Activity: onTouchEvent_ACTION_UP
結(jié)合輸出結(jié)果,總結(jié)如下:
當觸發(fā)ACTION_DOWN的時候,事件依然是從Activity開始一層層向下傳遞,當傳遞到MyViewGroupB時,因為進行了事件攔截,所以執(zhí)行完onInterceptTouchEvent后不再向下傳遞,而是直接交由MyViewGroupB的onTouchEvent進行消費處理。由于我們是只攔截,不消費,所以事件向上傳遞,交由上層處理,最終回到Activity。之后觸發(fā)的ACTION_MOVE和ACTION_UP也不再向下傳遞,直接交由Activity分發(fā)給自己處理。
3、我們還是將MyViewGroupB的onInterceptTouchEvent返回super,但是將他的onTouchEvent返回true。這意味著我們不攔截,但是由MyViewGroupB進行事件處理。
輸出結(jié)果如下:
I/Activity: dispatchTouchEvent_ACTION_DOWNI/MyViewGroupA: dispatchTouchEvent_ACTION_DOWNI/MyViewGroupA: onInterceptTouchEvent_ACTION_DOWNI/MyViewGroupB: dispatchTouchEvent_ACTION_DOWNI/MyViewGroupB: onInterceptTouchEvent_ACTION_DOWNI/MyView: dispatchTouchEvent_ACTION_DOWNI/MyView: onTouchEvent_ACTION_DOWNI/MyViewGroupB: onTouchEvent_ACTION_DOWNI/Activity: dispatchTouchEvent_ACTION_MOVEI/MyViewGroupA: dispatchTouchEvent_ACTION_MOVEI/MyViewGroupA: onInterceptTouchEvent_ACTION_MOVEI/MyViewGroupB: dispatchTouchEvent_ACTION_MOVEI/MyViewGroupB: onTouchEvent_ACTION_MOVEI/Activity: dispatchTouchEvent_ACTION_UPI/MyViewGroupA: dispatchTouchEvent_ACTION_UPI/MyViewGroupA: onInterceptTouchEvent_ACTION_UPI/MyViewGroupB: dispatchTouchEvent_ACTION_UPI/MyViewGroupB: onTouchEvent_ACTION_UP
結(jié)合輸出結(jié)果,總結(jié)如下:
可以看出,當觸發(fā)ACTION_DOWN的時候,事件的分發(fā)傳遞過程和1的時候一樣,從Activity開始一層層向下傳遞,最終傳遞到最底層MyView,觸發(fā)消費操作,然后MyView將消費操作移交上層處理,然后到達MyViewGroupB的onTouchEvent,并且進行了消費處理,事件處理到此不在向上移交。當觸發(fā)ACTION_MOVE和ACTION_UP操作時,事件依然需要由Activity開始向下分發(fā)傳遞,但是當傳遞到MyViewGroupB后,由于其消費了ACTION_DOWN,事件將不再繼續(xù)向下分發(fā),而是直接由MyViewGroupB分發(fā)給自己的onTouchEvent進行繼續(xù)處理。事件處理也不再向上移交。
4、將MyViewGroupB的onInterceptTouchEvent和onTouchEvent的返回值均改為true。這意味著既攔截,又消費。
輸出結(jié)果如下:
I/Activity: dispatchTouchEvent_ACTION_DOWNI/MyViewGroupA: dispatchTouchEvent_ACTION_DOWNI/MyViewGroupA: onInterceptTouchEvent_ACTION_DOWNI/MyViewGroupB: dispatchTouchEvent_ACTION_DOWNI/MyViewGroupB: onInterceptTouchEvent_ACTION_DOWNI/MyViewGroupB: onTouchEvent_ACTION_DOWNI/Activity: dispatchTouchEvent_ACTION_MOVEI/MyViewGroupA: dispatchTouchEvent_ACTION_MOVEI/MyViewGroupA: onInterceptTouchEvent_ACTION_MOVEI/MyViewGroupB: dispatchTouchEvent_ACTION_MOVEI/MyViewGroupB: onTouchEvent_ACTION_MOVEI/Activity: dispatchTouchEvent_ACTION_UPI/MyViewGroupA: dispatchTouchEvent_ACTION_UPI/MyViewGroupA: onInterceptTouchEvent_ACTION_UPI/MyViewGroupB: dispatchTouchEvent_ACTION_UPI/MyViewGroupB: onTouchEvent_ACTION_UP
結(jié)合輸出結(jié)果,總結(jié)如下:
當觸發(fā)ACTION_DOWN的時候,依然從Activity開始向下傳遞,當?shù)竭_MyViewGroupB的是,因為在onInterceptTouchEvent進行了攔截操作,因此不再繼續(xù)向下分發(fā)傳遞,而是交由MyViewGroupB的onTouchEvent進行處理消費。MyViewGroupB的onTouchEvent返回的是true,說明它決定對ACTION_DOWN進行處理,因此事件也就不再移交上層處理。當觸發(fā)ACTION_MOVE和ACTION_UP的時候,事件還是從Activity開始向下傳遞,當?shù)竭_MyViewGroupB的時候,由于之前進行了攔截操作,因此,MyViewGroupB直接將事件分發(fā)給自己的onTouchEvent進行處理,不在向下分發(fā)傳遞。事件處理也不再向上層移交。
三、文末
事件分發(fā)機制就猶如數(shù)學的定理是一樣道理的,只有記住定理,才能在具體應用中具體分析,有人可能不知道在什么地方會用到,如果你做的項目中,比如有一個控件點擊不能反應,那么就有可能是事件分發(fā)的結(jié)果。
至于對源碼的分析可能內(nèi)容比較復雜,內(nèi)容也多;需要我這套framework進階學習全套筆記的可以前往私信我;發(fā)送“核心筆記”或“手冊”即可領(lǐng)?。≌聿灰?!記得點贊分享哦!