一.簡(jiǎn)述
android上所有的事件操作都是基于用戶對(duì)屏幕的觸摸與滑動(dòng)進(jìn)行分解,進(jìn)而對(duì)用戶不同的操作進(jìn)行監(jiān)聽(tīng);如:點(diǎn)擊事件、雙擊事件、長(zhǎng)按事件等等。
一次完整的事件傳遞主要包含三個(gè)階段,分別是事件的分發(fā)(Dispatch)、攔截(Intercept)、消費(fèi)(Consume)。
關(guān)鍵詞
- ACTION_DOWN:用戶手指按下屏幕:
- ACTIOJN_MOVE:用戶手指在屏幕上滑動(dòng),滑動(dòng)的距離大于閥值,即產(chǎn)生滑動(dòng)事件;
- ACTiON_UP:用戶手指離開(kāi)屏幕:
- 分發(fā)(Dispatch):事件分發(fā),即dispatchTouchEvent();在android中所有的觸摸事件都有由dispatchTouchEvent()進(jìn)行分發(fā)處理的。返回true事件被消費(fèi),返回super.dispatchTouchEvent(),會(huì)觸發(fā)攔截機(jī)制;
- 攔截(Intercept): 事件攔截,即onInterceptTouchEvent();該方法,只在ViewGroup及其子類(lèi)中存在,view和Activity 中不存在。實(shí)現(xiàn)邏輯,返回true,被攔截,返回false或super.onInterceptTouchEvent(),向下傳遞;
- 消費(fèi)(Consume):事件消費(fèi),即onTouchEvent();返回true,事件被消費(fèi)掉(處理),不會(huì)向上傳遞給父視圖。返回false,事件不會(huì)被處理,向上傳遞給父視圖,進(jìn)行事件分發(fā);
視圖與事件的對(duì)應(yīng)
| 視圖 | 分發(fā) | 攔截 | 消費(fèi) |
|---|---|---|---|
| Activity | dispatchTouchEvent | -- | onTouchEvent |
| ViewGroup | dispatchTouchEvent | onInterceptTouchEvent | onTouchEvent |
| View | dispatchTouchEvent | -- | onTouchEvent |
二.View的事件傳遞
代碼
- Activity:EventActivity
public class EventActivity extends AppCompatActivity {
private static final String TAG = EventActivity.class.getSimpleName();
EventTextView etvEvent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event);
etvEvent = (EventTextView) findViewById(R.id.etv_event);
etvEvent.setOnClickListener(onClickListener);
etvEvent.setOnTouchListener(onTouchListener);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "dispatchTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "dispatchTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG, "dispatchTouchEvent ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.e(TAG, "dispatchTouchEvent ACTION_CANCEL");
break;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "onTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "onTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG, "onTouchEvent ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.e(TAG, "onTouchEvent ACTION_CANCEL");
break;
}
return super.onTouchEvent(event);
}
private View.OnClickListener onClickListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e(TAG, "onClickListener ONCLICK");
}
};
private View.OnTouchListener onTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "onTouchListener ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "onTouchListener ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG, "onTouchListener ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.e(TAG, "onTouchListener ACTION_CANCEL");
break;
}
return false;
}
};
}
- 自定義View:EventTextView
public class EventTextView extends AppCompatTextView {
private static final String TAG = EventTextView.class.getSimpleName();
public EventTextView(Context context) {
super(context);
}
public EventTextView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public EventTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "dispatchTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "dispatchTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG, "dispatchTouchEvent ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.e(TAG, "dispatchTouchEvent ACTION_CANCEL");
break;
}
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "onTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "onTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG, "onTouchEvent ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.e(TAG, "onTouchEvent ACTION_CANCEL");
break;
}
return super.onTouchEvent(event);
}
}
- 布局文件:activity_event.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="study.zxh.com.androidstudydemp.event.EventActivity">
<study.zxh.com.androidstudydemp.event.EventTextView
android:id="@+id/etv_event"
android:layout_width="match_parent"
android:layout_height="50dp" android:gravity="center_vertical"
android:text="android事件傳遞機(jī)制測(cè)試--view"
android:textSize="16dp" />
</LinearLayout>
代碼概述
相關(guān)代碼優(yōu)先級(jí):
Activity > ViewGroup > View
ACTION_DOWN > ACTION_MOVE > ACTION_UP
dispatchTouchEvent > OnTouchListener代碼結(jié)構(gòu)很簡(jiǎn)單,分別對(duì)Activity和View的dispatchTouchEvent以及onTouchEvent進(jìn)行的日志記錄;同時(shí),對(duì)常用的事件OnClickListener以及OnTouchListener進(jìn)行了記錄,對(duì)于得到的結(jié)果,可以劃分為以下幾種:
1.事件被activity 的dispatchTouchEvent消費(fèi):

dispatchTouchEvent消費(fèi)事件
結(jié)論
- 事件消費(fèi)后,不會(huì)再向下傳遞,也不會(huì)觸發(fā)activity的onTouchEvent();
2.事件被View 的OnTouchListener消費(fèi):

事件被View 的OnTouchListener消費(fèi)
結(jié)論
- 事件消費(fèi)后,不會(huì)觸發(fā)activty的onTouchEvent()方法;
- 事件消費(fèi)后,會(huì)觸發(fā)view綁定的OnTouchListener監(jiān)聽(tīng);
- 事件的傳遞,每次都是由Activity的dispatchTouchEvent開(kāi)始的;
- 事件的傳遞,OnTouchListener的優(yōu)先級(jí)高于onTouchEvent的優(yōu)先級(jí);
- 事件的監(jiān)聽(tīng)依賴與onTouchEvent方法;
3.事件被View的dispatchTouchEvent消費(fèi):

事件被View的dispatchTouchEvent消費(fèi)
結(jié)論
- 事件被消費(fèi),不會(huì)觸發(fā)任何的onTouchEvent()方法;
- 事件被消費(fèi),不會(huì)觸發(fā)任何的監(jiān)聽(tīng)事件;
4.事件被View的onTouchEvent消費(fèi):

事件被View的onTouchEvent消費(fèi)
結(jié)論
- 所有事件都會(huì)單向傳遞到View的onTouchEvent方法,但是不會(huì)再向上傳遞;
- 事件傳遞的順序是:
dispatchTouchEvent(Activity)-->dispatchTouchEvent(View)-->onTouchListener(View)-->onTouchEvent(View) - 沒(méi)有調(diào)用到onClickListener。
5.事件被Activity的onClickListener消費(fèi):

事件被Activity的onClickListener消費(fèi)
結(jié)論
- onClick事件發(fā)生在View的onTouchEvent方法之后,消費(fèi)掉就不會(huì)再向上傳遞;
- 事件傳遞的順序是:
dispatchTouchEvent(Activity)-->dispatchTouchEvent(View)-->onTouchListener(View)-->onTouchEvent(View)-->onClickListener
6.事件被Activity的onTouchEvent消費(fèi):

事件被Activity的onTouchEvent消費(fèi)
結(jié)論
- 會(huì)觸發(fā)Activity以及View的所有相關(guān)方法;
- 事件觸發(fā)的流程是以Activity的dispatchTouchEvent開(kāi)始,以activity的onTouchEvent結(jié)束
二.View的事件傳遞
代碼
- 自定義ViewGroup:EventLinearLayout
public class EventLinearLayout extends LinearLayout {
private static final String TAG = "EventLinearLayout";
public EventLinearLayout(Context context) {
super(context);
}
public EventLinearLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public EventLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "dispatchTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "dispatchTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG, "dispatchTouchEvent ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.e(TAG, "dispatchTouchEvent ACTION_CANCEL");
break;
}
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "onTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "onTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG, "onTouchEvent ACTION_UP");
break;
case MotionEvent.ACTION_CANCEL:
Log.e(TAG, "onTouchEvent ACTION_CANCEL");
break;
}
return super.onTouchEvent(event);
}
}
- 修改布局文件:activity_event.xml
<?xml version="1.0" encoding="utf-8"?>
<study.zxh.com.androidstudydemp.event.EventLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="study.zxh.com.androidstudydemp.event.EventActivity">
<study.zxh.com.androidstudydemp.event.EventTextView
android:id="@+id/etv_event"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center_vertical"
android:text="android事件傳遞機(jī)制測(cè)試--view"
android:textSize="16dp" />
</study.zxh.com.androidstudydemp.event.EventLinearLayout>
1.事件被ViewGroup的dispatchTouchEvent 消費(fèi)

事件被ViewGroup的dispatchTouchEvent 消費(fèi)
結(jié)論
- 事件被ViewGroup的dispatchTouchEvent消費(fèi),沒(méi)有觸發(fā)任何onTouchEvent();
2.ViewGroup的dispatchTouchEvent 返回false

ViewGroup的dispatchTouchEvent 返回false
結(jié)論
- 事件沒(méi)有被ViewGroup消費(fèi);
- 事件不會(huì)向下傳遞;
- 沒(méi)有調(diào)用ViewGroup的interceptTouchEvent(攔截器);
- 事件回傳到activity的onTouchEvent后事件結(jié)束;
3.ViewGroup的dispatchTouchEvent 返回super.dispatchTouchEvent()

ViewGroup的dispatchTouchEvent 返回super.dispatchTouchEvent()
結(jié)論
- 事件會(huì)正常向下傳遞,若沒(méi)有被消費(fèi),則會(huì)回傳給activity的onTouchEvent處理;
4. 事件被ViewGroup的interceptTouchEvent 攔截

事件被ViewGroup的interceptTouchEvent 消費(fèi)
結(jié)論
- 事件被攔截之后,會(huì)直接調(diào)用ViewGroup的onTouchEvent進(jìn)行處理;
- 事件被攔截,就不會(huì)再向下傳遞。
5. ViewGroup的interceptTouchEvent 返回 false

ViewGroup的interceptTouchEvent 返回 false
結(jié)論
- 當(dāng)返回false時(shí),事件正常向下傳遞,不會(huì)有任何影響;
6. ViewGroup的interceptTouchEvent 返回 super.onInterceptTouchEvent()

ViewGroup的interceptTouchEvent 返回 super.onInterceptTouchEvent()
結(jié)論
- 返回super.onInterceptTouchEvent(),顯示的結(jié)結(jié)果,與返回false相同;
問(wèn)題:若是多層ViewGroup嵌套,又是什么結(jié)果呢?
7. 事件被ViewGroup的onTouchEvent 消費(fèi)

事件被ViewGroup的onTouchEvent 消費(fèi)
結(jié)論
- 事件會(huì)先觸發(fā)View的onTouchEvent,然后再出發(fā)ViewGroup的TouchEvent;
8. ViewGroup的onTouchEvent 返回false或super.onTouchEvent()

ViewGroup的onTouchEvent 返回false

ViewGroup的onTouchEvent 返回super.onTouchEvent()
結(jié)論
- 兩種情況效果相同
結(jié)論
- 事件被ViewGroup攔截,沒(méi)有觸發(fā)任何onTouchEvent();
三. 分析返回false與super的區(qū)別
(在ViewGroup嵌套ViewGroup情況下)
代碼
- 修改布局文件:activity_event.xml
<?xml version="1.0" encoding="utf-8"?>
<study.zxh.com.androidstudydemp.event.EventLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="study.zxh.com.androidstudydemp.event.EventActivity">
<study.zxh.com.androidstudydemp.event.EventLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="study.zxh.com.androidstudydemp.event.EventActivity">
<study.zxh.com.androidstudydemp.event.EventTextView
android:id="@+id/etv_event"
android:layout_width="match_parent"
android:layout_height="50dp"
android:gravity="center_vertical"
android:text="android事件傳遞機(jī)制測(cè)試--view"
android:textSize="16dp" />
</study.zxh.com.androidstudydemp.event.EventLinearLayout>
</study.zxh.com.androidstudydemp.event.EventLinearLayout>
1.dispatchTouchEvent
activty的disptchTouchEvent返回false

activty的disptchTouchEvent返回false
activty的disptchTouchEvent返回super

activty的disptchTouchEvent返回super
ViewGroup的disptchTouchEvent返回false

ViewGroup的disptchTouchEvent返回false
ViewGroup的disptchTouchEvent返回super

ViewGroup的disptchTouchEvent返回super
View的disptchTouchEvent返回false

View的disptchTouchEvent返回false
View的disptchTouchEvent返回super

View的disptchTouchEvent返回super
2.interceptTouchEvent
interceptTouchEvent返回false

interceptTouchEvent返回false
interceptTouchEvent返回super

interceptTouchEvent返回super
3.onTouchEvent
onTouchEvent返回super

onTouchEvent返回super
onTouchEvent返回false

onTouchEvent返回false
總結(jié)
1.dispatchTouchEvent返回false,事件不會(huì)繼續(xù)向下傳遞,但是會(huì)向上調(diào)用,調(diào)用上一級(jí)的onTouchEvent,進(jìn)行處理;
2.dispatchTouchEvent返回super,事件會(huì)向下傳遞,調(diào)用相應(yīng)等級(jí)的dispatchTouchEvent或interceptTouchEvnt進(jìn)行繼續(xù)處理;
3.interceptTouchEvent與onTouchEvent返回false或super沒(méi)有區(qū)別;
備注:dispatchTouchEvent返回false和super,沒(méi)有區(qū)別,因?yàn)樗鼈儧](méi)有需要向下傳遞的途徑
四. 事件分發(fā)機(jī)制流程

事件分發(fā)機(jī)制流程
詳解
- 事件由activity的dispatchTouchEvent開(kāi)始,事件被消費(fèi)即結(jié)束;
- 事件沒(méi)有被消費(fèi),若返回false,直接調(diào)用上一級(jí)的onTouchEvent進(jìn)行消費(fèi);
- 若返回super,會(huì)執(zhí)行同級(jí)別下的相關(guān)方法,即如下兩種途徑:
1). 若是ViewGroup,先執(zhí)行interceptTouchEvent方法,返回true即攔截,運(yùn)行同級(jí)別onTouchEvent方法進(jìn)行消費(fèi);返回false或super,事件向下傳遞;
2). 若不是ViewGroup,事件向下傳遞,進(jìn)入view層,再次進(jìn)行事件分發(fā),若被消費(fèi),事件結(jié)束,若沒(méi)有被消費(fèi),事件會(huì)由View的onTouchEvent依次傳回到Activity的onTouchEvent方法最終結(jié)束;
完整的事件傳遞流程
dispatchTouchEvent(Activity)--> dispatchTouchEvent(ViewGroup)--> interceptTouchEvent(ViewGroup) -->dispatchTouchEvent(View)--> onTouchEvent(View) --> onTouchEvent(ViewGroup)--> onTouchEvent(Activity)