1、觸摸事件類型
觸摸事件對應的類爲MotionEvent類,對應的類型分爲ACTION_DOWN,ACTION_MOVE,ACTION_UP三種,按下觸發ACTION_DOWN,擡起觸發ACTION_UP,如果按下後手指不移動,則不會觸發ACTION_MOVE
2、事件傳遞的三個階段
一次完整的事件傳遞只要包含三個階段:分發(Dispatch)、攔截(Intercept)和消費(Consume)
分別對應的方法爲:dispatchTouchEvent(),onInterceptTouchEvent()和onTouchEvent()
Dispatch :在Android系統中所有的觸摸事件都是通過這個方法分發的,此方法會根據當前視圖的具體邏輯來決定是直接消費事件,還是將事件繼續進行傳遞給子視圖。方法返回true時表示被當前視圖消費掉,不會再繼續分發事件。返回false時要根據具體的情況而定,稍後會有流程圖。返回super.dispatchTouchEvent,表示繼續分發該事件。如果當前的視圖爲ViewGroup或其子類的話,則會調用onInterceptTouchEvent()方法,判斷是否攔截該事件。
Intercept :此方法只在ViewGroup中存在,在View和Activity中不存在。這個方法根據返回的布爾值來決定是否攔截對應的事件。返回true,表示攔截,不在進行分發。返回false或者是super.onInterceptTouchEvent,表示不行攔截繼續傳遞給子視圖。
Consume :此方法返回true時,表示當前的視圖可以處理此事件,事件將不會向上傳遞給父視圖,返回false,表示當前的視圖不會處理此事件,事件將向上傳遞,交於父視圖的onTouchEvent方法處理。
3、View的事件傳遞機制
View控件爲最小控件,不能再放入其他控件,擁有dispathchTouchEvent()和onTouchEvent()方法,爲了清楚的看出View控件的事件傳遞機制,定義一個繼承TextView的MyTextView控件,並進行事件的打印,同時定義一個Activity來展示MyTextView,併爲其設置點擊(onClick)和觸摸(onTouch)的監聽。代碼如下:
MyTextView:
public class MyTextView extends TextView {
public MyTextView(Context context) {
super(context);
}
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN :
StringUtils.showLog("MyTextView::dispatchTouchEvent::ACTION_DOWN");
break;
case MotionEvent.ACTION_UP :
StringUtils.showLog("MyTextView::dispatchTouchEvent::ACTION_UP");
break;
case MotionEvent.ACTION_MOVE :
StringUtils.showLog("MyTextView::dispatchTouchEvent::ACTION_MOVE");
break;
}
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN :
StringUtils.showLog("MyTextView::onTouchEvent::ACTION_DOWN");
break;
case MotionEvent.ACTION_UP :
StringUtils.showLog("MyTextView::onTouchEvent::ACTION_UP");
break;
case MotionEvent.ACTION_MOVE :
StringUtils.showLog("MyTextView::onTouchEvent::ACTION_MOVE");
break;
}
return super.onTouchEvent(event);
}
}
TouchEventActivitypublic class TouchEventActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_touchevent);
ButterKnife.bind(this);
}
@OnClick({R.id.TouchEvent_click_tv})
public void touchEventClick(View view) {
switch (view.getId()) {
case R.id.TouchEvent_click_tv:
StringUtils.showLog("MyTextView::onClick");
break;
}
}
@OnTouch({R.id.TouchEvent_click_tv})
public boolean touchEventTouch(View view, MotionEvent motionEvent) {
switch (view.getId()) {
case R.id.TouchEvent_click_tv:
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
StringUtils.showLog("MyTextView::onTouchACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
StringUtils.showLog("MyTextView::onTouchACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
StringUtils.showLog("MyTextView::onTouchACTION_UP");
break;
}
break;
}
return super.onTouchEvent(motionEvent);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
StringUtils.showLog("TouchEventActivity::dispatchTouchEvent::ACTION_DOWN");
break;
case MotionEvent.ACTION_UP:
StringUtils.showLog("TouchEventActivity::dispatchTouchEvent::ACTION_UP");
break;
case MotionEvent.ACTION_MOVE:
StringUtils.showLog("TouchEventActivity::dispatchTouchEvent::ACTION_MOVE");
break;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
StringUtils.showLog("TouchEventActivity::onTouchEvent::ACTION_DOWN");
break;
case MotionEvent.ACTION_UP:
StringUtils.showLog("TouchEventActivity::onTouchEvent::ACTION_UP");
break;
case MotionEvent.ACTION_MOVE:
StringUtils.showLog("TouchEventActivity::onTouchEvent::ACTION_MOVE");
break;
}
return super.onTouchEvent(event);
}
}
TouchEventActivity的Xml<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.fei.mystudy.touchenevt.MyTextView
android:id="@+id/TouchEvent_click_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="點擊"/>
</LinearLayout>
運行上面代碼,點擊MyTextView,打印的日誌如下:
04-25 15:43:28.552 29099-29099/com.fei.mystudy I/fei_std: TouchEventActivity::dispatchTouchEvent::ACTION_DOWN
04-25 15:43:28.558 29099-29099/com.fei.mystudy I/fei_std: MyTextView::dispatchTouchEvent::ACTION_DOWN
04-25 15:43:28.558 29099-29099/com.fei.mystudy I/fei_std: MyTextView::onTouchACTION_DOWN
04-25 15:43:28.558 29099-29099/com.fei.mystudy I/fei_std: MyTextView::onTouchEvent::ACTION_DOWN
04-25 15:43:28.647 29099-29099/com.fei.mystudy I/fei_std: TouchEventActivity::dispatchTouchEvent::ACTION_UP
04-25 15:43:28.647 29099-29099/com.fei.mystudy I/fei_std: MyTextView::dispatchTouchEvent::ACTION_UP
04-25 15:43:28.647 29099-29099/com.fei.mystudy I/fei_std: MyTextView::onTouchACTION_UP
04-25 15:43:28.647 29099-29099/com.fei.mystudy I/fei_std: MyTextView::onTouchEvent::ACTION_UP
04-25 15:43:28.648 29099-29099/com.fei.mystudy I/fei_std: MyTextView::onClick
從上面可以看出 dispathTouchEvent()和onTouchEvent()方法的返回值有如下三種情況:
1、返回true;
2、返回false;
3、返回父類的同名方法
不同返回值所造成的的結果不同,歸納總結如下(圖是直接截得圖):
4、ViewGroup的事件傳遞機制
ViewGroup爲View的容器,只要是能放入View控件的控件,都爲ViewGroup或其子類,ViewGroup擁有三個方法,分別是dispathTouchEvent(),onInterceptTouchEvent()以及onTouchEvent(),現定義一個MyLinearLayout,繼承自LinearLayout,加入到上面的測試代碼中。如下:
MyLinearLayout
public class MyLinearLayout extends LinearLayout {
public MyLinearLayout(Context context) {
super(context);
}
public MyLinearLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN :
StringUtils.showLog("MyLinearLayout::dispatchTouchEvent::ACTION_DOWN");
break;
case MotionEvent.ACTION_UP :
StringUtils.showLog("MyLinearLayout::dispatchTouchEvent::ACTION_UP");
break;
case MotionEvent.ACTION_MOVE :
StringUtils.showLog("MyLinearLayout::dispatchTouchEvent::ACTION_MOVE");
break;
}
return super.dispatchTouchEvent(event);
// return false;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_DOWN :
StringUtils.showLog("MyLinearLayout::onInterceptTouchEvent::ACTION_DOWN");
break;
case MotionEvent.ACTION_UP :
StringUtils.showLog("MyLinearLayout::onInterceptTouchEvent::ACTION_UP");
break;
case MotionEvent.ACTION_MOVE :
StringUtils.showLog("MyLinearLayout::onInterceptTouchEvent::ACTION_MOVE");
break;
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN :
StringUtils.showLog("MyLinearLayout::onTouchEvent::ACTION_DOWN");
break;
case MotionEvent.ACTION_UP :
StringUtils.showLog("MyLinearLayout::onTouchEvent::ACTION_UP");
break;
case MotionEvent.ACTION_MOVE :
StringUtils.showLog("MyLinearLayout::onTouchEvent::ACTION_MOVE");
break;
}
return super.onTouchEvent(event);
// return true;
}
}
TouchEventActivity的代碼不變,只是佈局文件要進行更改,如下:<?xml version="1.0" encoding="utf-8"?>
<com.fei.mystudy.touchenevt.MyLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.fei.mystudy.touchenevt.MyTextView
android:id="@+id/TouchEvent_click_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="點擊"/>
</com.fei.mystudy.touchenevt.MyLinearLayout>
之後運行,打印信息如下:
04-25 16:03:58.022 29099-29099/com.fei.mystudy I/fei_std: TouchEventActivity::dispatchTouchEvent::ACTION_DOWN
04-25 16:03:58.026 29099-29099/com.fei.mystudy I/fei_std: MyLinearLayout::dispatchTouchEvent::ACTION_DOWN
04-25 16:03:58.026 29099-29099/com.fei.mystudy I/fei_std: MyLinearLayout::onInterceptTouchEvent::ACTION_DOWN
04-25 16:03:58.026 29099-29099/com.fei.mystudy I/fei_std: MyTextView::dispatchTouchEvent::ACTION_DOWN
04-25 16:03:58.026 29099-29099/com.fei.mystudy I/fei_std: MyTextView::onTouchACTION_DOWN
04-25 16:03:58.026 29099-29099/com.fei.mystudy I/fei_std: MyTextView::onTouchEvent::ACTION_DOWN
04-25 16:03:58.097 29099-29099/com.fei.mystudy I/fei_std: TouchEventActivity::dispatchTouchEvent::ACTION_UP
04-25 16:03:58.097 29099-29099/com.fei.mystudy I/fei_std: MyLinearLayout::dispatchTouchEvent::ACTION_UP
04-25 16:03:58.097 29099-29099/com.fei.mystudy I/fei_std: MyLinearLayout::onInterceptTouchEvent::ACTION_UP
04-25 16:03:58.097 29099-29099/com.fei.mystudy I/fei_std: MyTextView::dispatchTouchEvent::ACTION_UP
04-25 16:03:58.097 29099-29099/com.fei.mystudy I/fei_std: MyTextView::onTouchACTION_UP
04-25 16:03:58.097 29099-29099/com.fei.mystudy I/fei_std: MyTextView::onTouchEvent::ACTION_UP
04-25 16:03:58.097 29099-29099/com.fei.mystudy I/fei_std: MyTextView::onClick
通過實驗,打印日誌信息得到如下流程圖(直接截取的別人的圖,稍微有點出入,不影響理解):
5、總結
通過上面的代碼和驗證,得到如下的結論:
1、事件傳遞由Activity傳遞到ViewGroup,在由ViewGroup進行遞歸傳遞給它的子View。
2、在子View進行事件的消費後,ViewGroup將收不到事件。
3、ViewGroup在onInterceptTouchEvent()方法中,返回爲true,則會對事件進行攔截,子View不會收到事件,如果返回false或父類方法的時候事件會繼續傳遞給子控件。