一切威、什么是事件序列
事件序列是指手指觸摸手機(jī)屏幕所產(chǎn)生的一系列行為腊嗡。
在Android中這些行為都封裝在MotionEvent中,下面列舉一些行為 :
(加粗常見(jiàn))
1. 單點(diǎn)觸控的行為:
- ACTION_DOWN :手指按下屏幕的一瞬間晓避,是事件序列的開(kāi)始
- ACTION_UP :手指離開(kāi)屏幕的瞬間交排,是事件序列的結(jié)束
- ACTION_MOVE :手指在屏幕移動(dòng)
- ACTION_CANCEL :行為取消,可以視為UP事件扑媚,但不執(zhí)行通常執(zhí)行的任何操作腰湾。在事件被上層攔截時(shí),上層View回收事件處理權(quán)的時(shí)候觸發(fā)费坊。
- ACTION_OUTSIDE :行為移動(dòng)到控件之外的位置倒槐,不提供完整手勢(shì),僅提供移動(dòng)/觸摸的初始位置附井。在手指不在控件區(qū)域時(shí)觸發(fā)导犹。這個(gè)行為一般會(huì)在Dialog或者懸浮窗的場(chǎng)景觸發(fā)。
2. 多點(diǎn)觸控的行為:
在多點(diǎn)觸控中羡忘,對(duì)多個(gè)手指進(jìn)行了編號(hào)谎痢,第一次按下的手指特殊處理作為主指針,其余的作為輔助指針卷雕,接下來(lái)的行為就是為輔助指針設(shè)計(jì)的节猿。(單點(diǎn)觸控的行為依舊會(huì)觸發(fā),但是是主指針觸發(fā)的)
- ACTION_POINTER_DOWN :非主要的手指按下屏幕
- ACTION_POINTER_UP :非主要的手指離開(kāi)屏幕
3. 鼠標(biāo)事件的行為:
ACTION_HOVER_ENTER漫雕、ACTION_HOVER_MOVE滨嘱、ACTION_HOVER_EXIT、ACTION_SCROLL
事件序列就是以DOWN開(kāi)頭浸间,UP結(jié)尾太雨,中間有無(wú)數(shù)MOVE的一系列行為。
二魁蒜、事件的傳遞規(guī)則
事件分發(fā)的順序是一個(gè)老生常談的點(diǎn)囊扳,也是面試幾乎都會(huì)問(wèn)到的知識(shí),這里不主要講事件分發(fā)的順序兜看,簡(jiǎn)單帶一下锥咸。
事件分發(fā)的順序:Activity -> Window(PhoneWindow實(shí)現(xiàn)) -> ViewGroup -> 子View
事件分發(fā)的順序是一個(gè)U型的順序,如果事件分發(fā)到最后依舊沒(méi)有任何一個(gè)View消耗這個(gè)時(shí)間细移,最終這個(gè)事件會(huì)交給Activity的onTouchEvent來(lái)處理搏予。
三、事件序列中不同行為的事件分發(fā)
上代碼~
這里根據(jù)事件分發(fā)的順序弧轧,封裝了一個(gè)MyViewGroup雪侥,以及子View LoadView,觀察它們的事件分發(fā):
MyViewFroup :
public class MyViewGroup extends LinearLayout {
……
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(this.getClass().toString(), "onTouchEvent: "+getS(event));
return super.onTouchEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d(this.getClass().toString(), "dispatchTouchEvent: "+getS(ev));
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.d(this.getClass().toString(), "onInterceptTouchEvent: "+getS(ev));
return super.onInterceptTouchEvent(ev);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
……
private String getS(MotionEvent event){
String s= "";
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
s = "ACTION_DOWN";
break;
case MotionEvent.ACTION_MOVE:
s = "ACTION_MOVE";
break;
case MotionEvent.ACTION_UP:
s = "ACTION_UP";
break;
}
return s;
}
}
LoadingView :
public class LoadingView extends View {
……
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(this.getClass().toString(), "onTouchEvent: "+getS(event));
return super.onTouchEvent(event);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.d(this.getClass().toString(), "dispatchTouchEvent: "+getS(event));
return super.dispatchTouchEvent(event);
}
private String getS(MotionEvent event){
String s= "";
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
s = "ACTION_DOWN";
break;
case MotionEvent.ACTION_MOVE:
s = "ACTION_MOVE";
break;
case MotionEvent.ACTION_UP:
s = "ACTION_UP";
break;
}
return s;
}
}
MainActivity :
public class MainActivity extends AppCompatActivity {
……
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.d(this.getClass().toString(), "dispatchTouchEvent: "+getS(ev));
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.d(this.getClass().toString(), "onTouchEvent: "+getS(event));
return super.onTouchEvent(event);
}
private String getS(MotionEvent event){
String s= "";
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
s = "ACTION_DOWN";
break;
case MotionEvent.ACTION_MOVE:
s = "ACTION_MOVE";
break;
case MotionEvent.ACTION_UP:
s = "ACTION_UP";
break;
}
return s;
}
}
在代碼中分別為Activity精绎,ViewGroup速缨,View的事件分發(fā)的方法都打上了log,接下來(lái)我們來(lái)看看整個(gè)事件的傳遞序列是怎樣的捺典。
1. 每個(gè)view都不處理事件
跑一下上面的代碼鸟廓,查看log:
打出了一個(gè)小長(zhǎng)串的log,我們分著來(lái)看一看。先看DOWN行為:
可以看到引谜,首先通過(guò)dispatchTouchEvent將事件按序依次分發(fā):MainActivity -> MyViewGroup (ViewGroup的onInterceptTouchEvent用來(lái)判斷是否攔截該事件)-> LoadingView
之后到達(dá)最底層的子View后依舊沒(méi)有消耗事件牍陌,通過(guò)onTouchEvent依次返回:LoadingView -> MyViewGroup -> MainActivity,最后還是交還給了Activity的TouchEvent()
再來(lái)看看MOVE事件:
可以看到,MOVE行為并沒(méi)有繼續(xù)向下傳遞员咽,而是直接分發(fā)給了Activity毒涧,并且由Activity的onTouchEvent()處理。
再來(lái)看看UP事件:
UP行為和MOVE一樣贝室,直接交給了Activity的onTouchEvent()處理契讲。
2.傳遞途中有View處理事件
可以看到,DOWN行為的傳遞終點(diǎn)決定了后續(xù)行為的傳遞終點(diǎn)滑频,后續(xù)的行為會(huì)直接分發(fā)到終點(diǎn)進(jìn)行處理捡偏。但是是不是只有不處理事件的時(shí)候是這樣的?我們來(lái)看看:
我們?cè)赩iewGroup層對(duì)事件進(jìn)行處理( onTouchEvent()返回true ):
通過(guò)log可以看到峡迷,處理事件情況下的結(jié)論和上邊的結(jié)果是一樣的银伟。當(dāng)ViewGroup的onTouchEvent處理了事件之后,后續(xù)事件會(huì)直接分發(fā)到ViewGroup的onTouchEvent绘搞。
四彤避、onInterceptTouchEvent和onTouchEvent對(duì)事件序列的不同影響
在上邊的分析中主要以onTouchEvent處理事件分析事件序列的事件分發(fā)順序,但是同樣影響著事件分發(fā)順序的onInterceptTouchEvent是否和onTouchEvent一致夯辖?
我們都知道在ViewGroup的onInterceptTouchEvent決定著是否攔截事件琉预,它的默認(rèn)值是false,在一些場(chǎng)景中我們也常通過(guò)重寫(xiě)onInterceptTouchEvent來(lái)解決一些事件的沖突蒿褂,比較常見(jiàn)的就是滑動(dòng)沖突圆米。
打log分析:
將MyViewGroup的onInterceptTouchEvent返回值指定為true,攔截事件贮缅。
首先來(lái)看DOWN行為:
通過(guò)log可以看出來(lái)榨咐,在onInterceptTouchEvent攔截了事件之后,dispatchTouchEvent不會(huì)將事件繼續(xù)向下傳遞谴供,而是交給了MyViewGroup的onTouchEvent,MyViewGroup的onTouchEvent并沒(méi)有處理這個(gè)事件齿坷,于是又向上傳遞事件桂肌,最終還是交給了Activity的onTouchEvent,事件的終點(diǎn)還是Activity的onTouchEvent永淌。
再來(lái)看看MOVE和UP行為:
看到log,一目了然,全是在Activity中處理的么介。說(shuō)明onInterceptTouchEvent對(duì)事件的終點(diǎn)不產(chǎn)生影響鹃答。
總結(jié)一下:onInterceptTouchEvent對(duì)事件序列的影響只影響事件分發(fā)的路徑并不影響事件分發(fā)的終點(diǎn),最終事件分發(fā)到哪,還是由onTouchEvent是否處理這個(gè)事件決定
到這里螃宙,事件分發(fā)中比較詳細(xì)的點(diǎn)就分析的差不多了蛮瞄,具體的事件分發(fā)機(jī)制的原理可以研究事件分發(fā)的源碼,這里不多做贅述谆扎,事件序列的不同行為的事件分發(fā)順序也是在面試中一個(gè)比較細(xì)的知識(shí)點(diǎn)挂捅,這篇筆記也算是之前自己在學(xué)習(xí)事件分發(fā)時(shí)候漏掉的細(xì)節(jié)補(bǔ)的坑~
學(xué)無(wú)止境,邊走邊補(bǔ) _ (:з」∠) _