一.簡(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及其子類中存在鸥咖,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)