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);
}
}
TouchEventActivity
public 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或父类方法的时候事件会继续传递给子控件。