前言:在Android开发中,经常会遇到触摸事件冲突,比如ViewPager的轮播图跟Fragment的划动事件冲突,或者轮播图跟下拉事件冲突,自定义view的事件处理等,本文章将会详细介绍Activity、View、ViewGroup三者的触摸事件传递机制,传递包括三个阶段:分发、拦截、消费。
本文章将会详细介绍Activity、View、ViewGroup三者的触摸事件传递机制,传递包括三个阶段:分发、拦截、消费。
触摸事件对应的是 MotionEvent 类,事件类型主要有三种:
注 :如果用户仅仅的是点击而已,则只会执行到 ACTION_DOWN 和 ACTION_UP 两个事件,不会执行到 ACTION_MOVE 事件。所以 ACTION_DOWN 和 ACTION_UP 是事件是必须的。
在Android系统中所有的触摸事件都是由 dispatchTouchEvent 方法进行分发的。该方法中判断事件是被消费( return true ),还是继续分发给子视图处理( return super.dispatchTouchEvent ),如果当前视图是ViewGroup或者其子类,则会调用 onInterceptTouchEvent 判断是否截拦。
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}
事件的截拦 InterceptTouchEvent 只存在于ViewGroup及其子类,activity和View是不存在该方法。该方法判断事件是被截拦 ( return true )并交给自身的 OnToucEvent 方法进行消费,还是继续传递给子视图( return super.InterceptTouchEvent 或者 return false )。
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return super.onInterceptTouchEvent(ev);
}
事件的消费通过 OnTouchEvent 方法判断,是被消费( return true ),还是不处理( return false )并将事件传递给父视图的 OnTouchEvent 方法进行处理。
@Override
public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}
所有拥有事件传递能力的类:
Activity: 拥有dispatchTouchEvent 、OnTouchEvent
ViewGroup: 拥有dispatchTouchEvent 、OnInterceptTouchEvent 、OnTouchEvent
View:拥有dispatchTouchEvent 、OnTouchEvent
虽然说ViewGroup是View的子类,但是这是说的View指的是除ViewGroup之外的View控件子类,首先定义一个MyTextView继承TextView,打印每次事件的触发以变了解事件传递的流程。
MyTextView 类
public class MyTextView extends TextView {
private String tag = "MyTextView";
public MyTextView(Context context) {
super(context);
}
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_UP:
Log.i(tag, "dispatchTouchEvent ACTION_UP");
break;
case MotionEvent.ACTION_MOVE:
Log.i(tag, "dispatchTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_DOWN:
Log.i(tag, "dispatchTouchEvent ACTION_DOWN");
break;
}
return super.dispatchTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_UP:
Log.i(tag, "onTouchEvent ACTION_UP");
break;
case MotionEvent.ACTION_MOVE:
Log.i(tag, "onTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_DOWN:
Log.i(tag, "onTouchEvent ACTION_DOWN");
break;
}
return super.onTouchEvent(event);
}
}
定义一个MainActivity来展现这个MyTextView,同时设置点击(onClick)和触摸(onTouch)监听。 MainActivity 类
public class MainActivity extends AppCompatActivity implements View.OnClickListener,View.OnTouchListener{
private MyTextView mMyTextView;
private String tag = "MainActiviy";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMyTextView = findViewById(R.id.text_view);
// 点击监听
mMyTextView.setOnClickListener(this);
// 触碰监听
mMyTextView.setOnTouchListener(this);
}
// MyTextView 点击事件
@Override
public void onClick(View view) {
switch (view.getId()){
case R.id.text_view:
Log.i(tag, "MyTextView onClick");
break;
}
}
// MyTextView 触碰事件
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()){
case MotionEvent.ACTION_UP:
Log.i(tag, "MyTextView onTouch ACTION_UP");
break;
case MotionEvent.ACTION_MOVE:
Log.i(tag, "MyTextView onTouch ACTION_MOVE");
break;
case MotionEvent.ACTION_DOWN:
Log.i(tag, "MyTextView onTouch ACTION_DOWN");
break;
}
return false;
}
// Activity 的事件分发
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_UP:
Log.i(tag, "dispatchTouchEvent ACTION_UP");
break;
case MotionEvent.ACTION_MOVE:
Log.i(tag, "dispatchTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_DOWN:
Log.i(tag, "dispatchTouchEvent ACTION_DOWN");
break;
}
return super.dispatchTouchEvent(ev);
}
// Activity 的事件消费
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_UP:
Log.i(tag, "onTouchEvent ACTION_UP");
break;
case MotionEvent.ACTION_MOVE:
Log.i(tag, "onTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_DOWN:
Log.i(tag, "onTouchEvent ACTION_DOWN");
break;
}
return super.onTouchEvent(event);
}
}
运行后,点击Text View反馈的打印日志
03-28 08:05:14.824 1219-1219/com.mvp.chenzhesheng.androidadvance I/MainActiviy: dispatchTouchEvent ACTION_DOWN 03-28 08:05:14.824 1219-1219/com.mvp.chenzhesheng.androidadvance I/MyTextView: dispatchTouchEvent ACTION_DOWN 03-28 08:05:14.824 1219-1219/com.mvp.chenzhesheng.androidadvance I/MainActiviy: MyTextView onTouch ACTION_DOWN 03-28 08:05:14.824 1219-1219/com.mvp.chenzhesheng.androidadvance I/MyTextView: onTouchEvent ACTION_DOWN 03-28 08:05:15.034 1219-1219/com.mvp.chenzhesheng.androidadvance I/MainActiviy: dispatchTouchEvent ACTION_UP 03-28 08:05:15.034 1219-1219/com.mvp.chenzhesheng.androidadvance I/MyTextView: dispatchTouchEvent ACTION_UP 03-28 08:05:15.034 1219-1219/com.mvp.chenzhesheng.androidadvance I/MainActiviy: MyTextView onTouch ACTION_UP 03-28 08:05:15.034 1219-1219/com.mvp.chenzhesheng.androidadvance I/MyTextView: onTouchEvent ACTION_UP 03-28 08:05:15.044 1219-1219/com.mvp.chenzhesheng.androidadvance I/MainActiviy: MyTextView onClick
dispatchTouchEvent 、 OnTouchEvent 这两个方法的返回值存在三种情况:
由于拥有不同的返回值,所以事件传递流程也有不同,经过不断修改返回值测试,最终得到了点击事件的流程图,ACTION_DOWN 和 ACTION_UP 事件的传递流程是相同的。
从上面的流程图可以得出结论:
ViewGroup是 View 的控件容器存在,拥有 dispatchTouchEvent 、 onInterceptTouchEvent 和 onTouchEvent 三个方法,比 View 多了一个 onInterceptTouchEvent 方法。为了更好的观察,我们需要自定义 MyRelativeLayout 继承 RelativeLayout 。
MyRelativeLayout类
public class MyRelativeLayout extends RelativeLayout {
private final static String tag = "MyRelativeLayout";
public MyRelativeLayout(Context context) {
super(context);
}
public MyRelativeLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_UP:
Log.i(tag, "dispatchTouchEvent ACTION_UP");
break;
case MotionEvent.ACTION_MOVE:
Log.i(tag, "dispatchTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_DOWN:
Log.i(tag, "dispatchTouchEvent ACTION_DOWN");
break;
}
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_UP:
Log.i(tag, "onInterceptTouchEvent ACTION_UP");
break;
case MotionEvent.ACTION_MOVE:
Log.i(tag, "onInterceptTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_DOWN:
Log.i(tag, "onInterceptTouchEvent ACTION_DOWN");
break;
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_UP:
Log.i(tag, "onTouchEvent ACTION_UP");
break;
case MotionEvent.ACTION_MOVE:
Log.i(tag, "onTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_DOWN:
Log.i(tag, "onTouchEvent ACTION_DOWN");
break;
}
return super.onTouchEvent(event);
}
}
main_activity.xml 文件
<?xml version="1.0" encoding="utf-8"?
<com.mvp.chenzhesheng.androidadvance.MyRelativeLayout
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"
<com.mvp.chenzhesheng.androidadvance.MyTextView
android:id="@+id/text_view"
android:clickable="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
</com.mvp.chenzhesheng.androidadvance.MyRelativeLayout
04-02 08:47:57.980 1030-1030/com.mvp.chenzhesheng.androidadvance I/MainActiviy: dispatchTouchEvent ACTION_DOWN 04-02 08:47:58.000 1030-1030/com.mvp.chenzhesheng.androidadvance I/MyRelativeLayout: dispatchTouchEvent ACTION_DOWN 04-02 08:47:58.000 1030-1030/com.mvp.chenzhesheng.androidadvance I/MyRelativeLayout: onInterceptTouchEvent ACTION_DOWN 04-02 08:47:58.000 1030-1030/com.mvp.chenzhesheng.androidadvance I/MyTextView: dispatchTouchEvent ACTION_DOWN 04-02 08:47:58.010 1030-1030/com.mvp.chenzhesheng.androidadvance I/MainActiviy: MyTextView onTouch ACTION_DOWN 04-02 08:47:58.010 1030-1030/com.mvp.chenzhesheng.androidadvance I/MyTextView: onTouchEvent ACTION_DOWN 04-02 08:47:58.200 1030-1030/com.mvp.chenzhesheng.androidadvance I/MainActiviy: dispatchTouchEvent ACTION_UP 04-02 08:47:58.200 1030-1030/com.mvp.chenzhesheng.androidadvance I/MyRelativeLayout: dispatchTouchEvent ACTION_UP 04-02 08:47:58.200 1030-1030/com.mvp.chenzhesheng.androidadvance I/MyRelativeLayout: onInterceptTouchEvent ACTION_UP 04-02 08:47:58.200 1030-1030/com.mvp.chenzhesheng.androidadvance I/MyTextView: dispatchTouchEvent ACTION_UP 04-02 08:47:58.210 1030-1030/com.mvp.chenzhesheng.androidadvance I/MainActiviy: MyTextView onTouch ACTION_UP 04-02 08:47:58.210 1030-1030/com.mvp.chenzhesheng.androidadvance I/MyTextView: onTouchEvent ACTION_UP 04-02 08:47:58.260 1030-1030/com.mvp.chenzhesheng.androidadvance I/MainActiviy: MyTextView onClick
可以看到 MainActivity 和 MyTextView 的事件传递处理中添加了一层 MyRelativeLayout 。通过不同返回值测试,得到一套流程图。
从上面的流程图可以得出结论:
以上就是本文的全部内容,希望对大家的学习有所帮助。