android群英传笔记——事件拦截机制(事件分发机制)

时间:2023-01-19 22:36:33

事件拦截机制

声明:对事件拦截机制的详细了解,除了群英传上的部分以外,还涵盖了开发艺术探索这本书上的内容,在开发艺术探索这本书上称之为事件分发机制。

什么是事件拦截机制?要了解事件拦截机制,首先要了解触摸事件。

触摸事件就是捕获触摸屏幕后产生的事件,Android为触摸事件封装了一个类——MotionEvent。当点击屏幕通常会产生两或三个事件——按下(MotionEvent.ACTION_DOWN),滑动(MotionEvent.ACTION_MOVE),抬起手(MotionEvent.ACTION_UP)。重写onTouchEvent()方法,其参数就是MotionEvent,只要是重写触摸相关的方法,参数一般都含有MotionEvent,可见其重要性。

而所谓的事件拦截机制,其实就是对MotionEvent事件的拦截和分发过程,即当一个MotionEvent产生了以后,系统需要把这个事件传递给具体的View,而这个过程就是事件的拦截和分发过程。要完成这个过程需要三个很重要的方法来共同完成:dispatchTouchEvent、onInterceptTouchEvent和onTouchEvent,先来介绍下这几个方法吧!
    public boolean dispatchTouchEvent(MotionEvent ev)
用来进行事件的分发。如果事件能够传递给当前View,那么此方法一定会被调用,返回结果受当前View的onTouchEvent和下级View的dispatchTouchEvent方法的影响,表示是否消耗当前事件。
    public boolean onInterceptTouchEvent(MotionEvent event)
在上述方法内部调用,用来判断是否拦截某个事件,如果当前View拦截某个事件,那么在同一个事件序列当中,此方法不会被再次调用,返回结果表示是否拦截当前事件。
    public boolean onTouchEvent(MotionEvent event)
在dispatchTouchEvent方法中调用,用来处理点击事件,返回结果表示是否消耗当前事件,如果不消耗,则在同一个事件序列中,当前View无法再次接收到事件。

它们的关系?请看以下的伪代码:
    public boolean dispatchTouchEvent(MotionEvent ev) {
boolean consume = false;
if(onInterceptTouchEvent(ev)) {
consume = onTouchEvent(ev);
} else {
consume = child.dispatchTouchEvent(ev);
}
return consume;
}
通过以上伪代码我们可以知道点击事件的传递规则:
对于一个根ViewGroup来说,点击事件产生后,首先会传递给它,这时它的dispatchTouchEvent就会被调用,如果这个ViewGroup的onInterceptTouchEvent方法返回true就表示它要拦截当前事件,接着事件就会交给这个ViewGroup处理,即它的onTouchEvent方法就会被调用;如果这个ViewGroup的onInterceptTouchEvent方法返回false就表示它不拦截当前事件,这时当前事件就会继续传递给它的子元素,接着子元素的dispatchTouchEvent方法就会被调用,如此反复知道事件被最终处理。

在以上理论的基础上进行进一步的实践,其实实践过程非常简单,就是重写了事件拦截和处理的几个方法,并通过log的方式观察几个方法的执行过程。

完成实践需要两个ViewGroup和一个View,分别给它们命名,最外层的ViewGroup为MyViewGroupA,中间ViewGroup为MyViewGroupB,最底层的View为MyView。本例的整个布局如图所示:

android群英传笔记——事件拦截机制(事件分发机制)

对于ViewGroup来说,重写了如下所示的三个方法:
    @Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.i(TAG, "MyViewGroupA_dispatchTouchEvent: " + ev.getAction());
boolean result = super.dispatchTouchEvent(ev);
Log.i(TAG, "MyViewGroupA_dispatchTouchEvent_after: " + result);
return result;
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.i(TAG, "MyViewGroupA_onInterceptTouchEvent: " + ev.getAction());
boolean result = super.onInterceptTouchEvent(ev);
Log.i(TAG, "MyViewGroupA_onInterceptTouchEvent_after: " + result);
return result;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG, "MyViewGroupA_onTouchEvent: " + event.getAction());
boolean result = super.onTouchEvent(event);
Log.i(TAG, "MyViewGroupA_onTouchEvent_after: " + result);
return result;
}
而对于View来说,重写了如下所示的两个方法:
    @Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG, "MyView_onTouchEvent: " + event.getAction());
boolean result = super.onTouchEvent(event);
Log.i(TAG, "MyView_onTouchEvent_after: " + result);
return result;
}

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.i(TAG, "MyView_dispatchTouchEvent: " + event.getAction());
boolean result = super.dispatchTouchEvent(event);
Log.i(TAG, "MyView_dispatchTouchEvent_after: " + result);
return result;
}
首先不修改任何值,只是点击一下View,然后看Log会怎样记录我们的操作和程序的响应。
        10-08 02:32:46.216 8213-8213/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupA_dispatchTouchEvent: 0
10-08 02:32:46.216 8213-8213/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupA_onInterceptTouchEvent: 0
10-08 02:32:46.217 8213-8213/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupA_onInterceptTouchEvent_after: false
10-08 02:32:46.217 8213-8213/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_dispatchTouchEvent: 0
10-08 02:32:46.217 8213-8213/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_onInterceptTouchEvent: 0
10-08 02:32:46.217 8213-8213/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_onInterceptTouchEvent_after: false
10-08 02:32:46.217 8213-8213/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyView_dispatchTouchEvent: 0
10-08 02:32:46.217 8213-8213/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyView_onTouchEvent: 0
10-08 02:32:46.217 8213-8213/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyView_onTouchEvent_after: false
10-08 02:32:46.217 8213-8213/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyView_dispatchTouchEvent_after: false
10-08 02:32:46.217 8213-8213/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_onTouchEvent: 0
10-08 02:32:46.218 8213-8213/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_onTouchEvent_after: false
10-08 02:32:46.218 8213-8213/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_dispatchTouchEvent_after: false
10-08 02:32:46.218 8213-8213/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupA_onTouchEvent: 0
10-08 02:32:46.218 8213-8213/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupA_onTouchEvent_after: false
10-08 02:32:46.218 8213-8213/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupA_dispatchTouchEvent_after: false
可见,正常情况下,事件的传递顺序是:
MyViewGroupA ——> MyViewGroupB ——> View
传递时先执行dispatchTouchEvent()方法,再执行onInterceptTouchEvent()方法。

事件的处理顺序是:
View ——> MyViewGroupA ——> MyViewGroupB
处理时都是执行onTouchEvent()方法。

这个日志也证实了伪代码的准确性。

可以将上面的整个事件过程整理成如图:

android群英传笔记——事件拦截机制(事件分发机制)

那么假设MyViewGroupB将事件拦截住了呢?会不会就是不执行MyView的相关事件方法呢?让我们试试给MyViewGroupB的onInterceptTouchEvent()方法返回true看看。
        10-08 02:59:34.694 31295-31295/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupA_dispatchTouchEvent: 0
10-08 02:59:34.694 31295-31295/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupA_onInterceptTouchEvent: 0
10-08 02:59:34.694 31295-31295/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupA_onInterceptTouchEvent_after: false
10-08 02:59:34.694 31295-31295/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_dispatchTouchEvent: 0
10-08 02:59:34.694 31295-31295/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_onInterceptTouchEvent: 0
10-08 02:59:34.694 31295-31295/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_onInterceptTouchEvent_after: true
10-08 02:59:34.694 31295-31295/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_onTouchEvent: 0
10-08 02:59:34.694 31295-31295/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_onTouchEvent_after: false
10-08 02:59:34.694 31295-31295/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_dispatchTouchEvent_after: false
10-08 02:59:34.694 31295-31295/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupA_onTouchEvent: 0
10-08 02:59:34.694 31295-31295/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupA_onTouchEvent_after: false
10-08 02:59:34.695 31295-31295/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupA_dispatchTouchEvent_after: false
和设想一样,MyView没事可干了。那么如果MyView处理事件时直接返回true,会怎么样呢?

修改MyView的代码如下所示:
    @Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG, "MyView_onTouchEvent: " + event.getAction());
super.onTouchEvent(event);
Log.i(TAG, "MyView_onTouchEvent_after: " + true);
return true;
}
再来看看Log,如下所示:
        10-08 05:53:24.361 2634-2634/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupA_dispatchTouchEvent: 0
10-08 05:53:24.361 2634-2634/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupA_onInterceptTouchEvent: 0
10-08 05:53:24.361 2634-2634/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupA_onInterceptTouchEvent_after: false
10-08 05:53:24.361 2634-2634/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_dispatchTouchEvent: 0
10-08 05:53:24.361 2634-2634/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_onInterceptTouchEvent: 0
10-08 05:53:24.361 2634-2634/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_onInterceptTouchEvent_after: false
10-08 05:53:24.361 2634-2634/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyView_dispatchTouchEvent: 0
10-08 05:53:24.361 2634-2634/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyView_onTouchEvent: 0
10-08 05:53:24.362 2634-2634/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyView_onTouchEvent_after: true
10-08 05:53:24.362 2634-2634/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyView_dispatchTouchEvent_after: true
10-08 05:53:24.362 2634-2634/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_dispatchTouchEvent_after: true
10-08 05:53:24.362 2634-2634/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupA_dispatchTouchEvent_after: true
10-08 05:53:24.442 2634-2634/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupA_dispatchTouchEvent: 1
10-08 05:53:24.442 2634-2634/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupA_onInterceptTouchEvent: 1
10-08 05:53:24.442 2634-2634/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupA_onInterceptTouchEvent_after: false
10-08 05:53:24.442 2634-2634/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_dispatchTouchEvent: 1
10-08 05:53:24.442 2634-2634/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_onInterceptTouchEvent: 1
10-08 05:53:24.442 2634-2634/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_onInterceptTouchEvent_after: false
10-08 05:53:24.442 2634-2634/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyView_dispatchTouchEvent: 1
10-08 05:53:24.442 2634-2634/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyView_onTouchEvent: 1
10-08 05:53:24.442 2634-2634/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyView_onTouchEvent_after: true
10-08 05:53:24.442 2634-2634/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyView_dispatchTouchEvent_after: true
10-08 05:53:24.442 2634-2634/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_dispatchTouchEvent_after: true
10-08 05:53:24.442 2634-2634/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupA_dispatchTouchEvent_after: true
这里会发现处理的过程好像变的更长了,其实这里给MyView的点击事件返回true就表示消耗了事件,就如上面介绍onTouchEvent方法时所描述的,消耗了就开始接收下次的事件。相信读者已经注意到了Log日志中getAction()返回的数值,那么这些数值代表着什么呢?
其实0表示ACTION_DOWN事件,1表示ACTION_UP事件,2表示ACTION_MOVE事件。
结合这些要点,在看面的Log日志,发现在执行ACTION_DOWN事件时,MyView的onTouchEvent方法返回true后,两个ViewGroup都不执行onTouchEvent方法,这也就是说MyView直接结束了这次事件,不再传回两个ViewGroup。但是,在结束了ACTION_DOWN事件后,紧接着开始执行了ACTION_UP事件,说明只有当MyView的onTouchEvent返回true才会执行下面的事件。

那么,恢复原代码后修改MyViewGroupB内的onTouchEvent返回值为true:
    @Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG, "MyViewGroupB_onTouchEvent: " + event.getAction());
super.onTouchEvent(event);
Log.i(TAG, "MyViewGroupB_onTouchEvent_after: " + true);
return true;
}
现在再来观察Log日志文件:
        10-08 06:31:49.333 9604-9604/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupA_dispatchTouchEvent: 0
10-08 06:31:49.333 9604-9604/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupA_onInterceptTouchEvent: 0
10-08 06:31:49.334 9604-9604/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupA_onInterceptTouchEvent_after: false
10-08 06:31:49.334 9604-9604/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_dispatchTouchEvent: 0
10-08 06:31:49.334 9604-9604/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_onInterceptTouchEvent: 0
10-08 06:31:49.334 9604-9604/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_onInterceptTouchEvent_after: false
10-08 06:31:49.334 9604-9604/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyView_dispatchTouchEvent: 0
10-08 06:31:49.334 9604-9604/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyView_onTouchEvent: 0
10-08 06:31:49.334 9604-9604/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyView_onTouchEvent_after: false
10-08 06:31:49.334 9604-9604/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyView_dispatchTouchEvent_after: false
10-08 06:31:49.334 9604-9604/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_onTouchEvent: 0
10-08 06:31:49.334 9604-9604/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_onTouchEvent_after: true
10-08 06:31:49.334 9604-9604/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_dispatchTouchEvent_after: true
10-08 06:31:49.334 9604-9604/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupA_dispatchTouchEvent_after: true
10-08 06:31:49.421 9604-9604/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupA_dispatchTouchEvent: 1
10-08 06:31:49.421 9604-9604/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupA_onInterceptTouchEvent: 1
10-08 06:31:49.421 9604-9604/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupA_onInterceptTouchEvent_after: false
10-08 06:31:49.421 9604-9604/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_dispatchTouchEvent: 1
10-08 06:31:49.421 9604-9604/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_onTouchEvent: 1
10-08 06:31:49.421 9604-9604/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_onTouchEvent_after: true
10-08 06:31:49.421 9604-9604/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupB_dispatchTouchEvent_after: true
10-08 06:31:49.421 9604-9604/com.example.eventinterceptionmechanism I/TestViewAndViewGroup: MyViewGroupA_dispatchTouchEvent_after: true
我们会发现结果和修改MyView的onTouchEvent方法的结果是一样的,也是返回true后直接结束事件,但是在第二次处理事件的时候,MyView就没再参与事件的处理,也就是说一旦onTouchEvent返回值为false就会导致在本次处理过程中后面的事件将不再分发给该View。

通过上面的分析,相信对事件的拦截,分发和处理有了一定的了解。

代码

那么最后再贴上测试的源代码:

MyViewGroupA.java
package com.example.eventinterceptionmechanism;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;

/**
* MyViewGroupA
* Created by shize on 2016/10/7.
*/

public class MyViewGroupA extends LinearLayout {

private final static String TAG = "TestViewAndViewGroup";

public MyViewGroupA(Context context) {
super(context);
}

public MyViewGroupA(Context context, AttributeSet attrs) {
super(context, attrs);
}

public MyViewGroupA(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.i(TAG, "MyViewGroupA_dispatchTouchEvent: " + ev.getAction());
boolean result = super.dispatchTouchEvent(ev);
Log.i(TAG, "MyViewGroupA_dispatchTouchEvent_after: " + result);
return result;
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.i(TAG, "MyViewGroupA_onInterceptTouchEvent: " + ev.getAction());
boolean result = super.onInterceptTouchEvent(ev);
Log.i(TAG, "MyViewGroupA_onInterceptTouchEvent_after: " + result);
return result;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG, "MyViewGroupA_onTouchEvent: " + event.getAction());
boolean result = super.onTouchEvent(event);
Log.i(TAG, "MyViewGroupA_onTouchEvent_after: " + result);
return result;
}
}
MyViewGroupB.java
package com.example.eventinterceptionmechanism;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;

/**
* MyViewGroupB
* Created by shize on 2016/10/7.
*/

public class MyViewGroupB extends LinearLayout {

private final static String TAG = "TestViewAndViewGroup";

public MyViewGroupB(Context context) {
super(context);
}

public MyViewGroupB(Context context, AttributeSet attrs) {
super(context, attrs);
}

public MyViewGroupB(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.i(TAG, "MyViewGroupB_dispatchTouchEvent: " + ev.getAction());
boolean result = super.dispatchTouchEvent(ev);
Log.i(TAG, "MyViewGroupB_dispatchTouchEvent_after: " + result);
return result;
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.i(TAG, "MyViewGroupB_onInterceptTouchEvent: " + ev.getAction());
boolean result = super.onInterceptTouchEvent(ev);
Log.i(TAG, "MyViewGroupB_onInterceptTouchEvent_after: " + result);
return result;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG, "MyViewGroupB_onTouchEvent: " + event.getAction());
boolean result = super.onTouchEvent(event);
Log.i(TAG, "MyViewGroupB_onTouchEvent_after: " + result);
return result;
}
}
MyView.java
package com.example.eventinterceptionmechanism;

import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.TextView;

/**
* MyView
* Created by shize on 2016/10/7.
*/

public class MyView extends TextView {

private final static String TAG = "TestViewAndViewGroup";

public MyView(Context context) {
super(context);
}

public MyView(Context context, AttributeSet attrs) {
super(context, attrs);
}

public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}

@Override
public boolean onTouchEvent(MotionEvent event) {
Log.i(TAG, "MyView_onTouchEvent: " + event.getAction());
boolean result = super.onTouchEvent(event);
Log.i(TAG, "MyView_onTouchEvent_after: " + result);
return result;
}

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.i(TAG, "MyView_dispatchTouchEvent: " + event.getAction());
boolean result = super.dispatchTouchEvent(event);
Log.i(TAG, "MyView_dispatchTouchEvent_after: " + result);
return result;
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<com.example.eventinterceptionmechanism.MyViewGroupA 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"
android:orientation="vertical"
android:gravity="center_horizontal"
tools:context="com.example.eventinterceptionmechanism.MainActivity">


<TextView
android:layout_width="match_parent"
android:layout_height="80dp"
android:textSize="24sp"
android:textAlignment="center"
android:text="@string/view_group_a"
android:textColor="@color/back"/>


<com.example.eventinterceptionmechanism.MyViewGroupB
android:layout_width="380dp"
android:layout_height="450dp"
android:orientation="vertical"
android:background="@color/back"
android:gravity="center_horizontal">


<TextView
android:layout_width="match_parent"
android:layout_height="120dp"
android:textSize="24sp"
android:textAlignment="center"
android:text="@string/view_group_b"
android:textColor="@color/write"/>


<com.example.eventinterceptionmechanism.MyView
android:layout_width="300dp"
android:layout_height="200dp"
android:textSize="24sp"
android:textAlignment="center"
android:text="@string/my_view"
android:background="@color/blue"
android:textColor="@color/write"/>


</com.example.eventinterceptionmechanism.MyViewGroupB>

</com.example.eventinterceptionmechanism.MyViewGroupA>
感谢阅读,学习重在坚持,贵在坚持,那么下次再见。