本文系转载文章,浏览原文可获取源码,文章开端有原文链接

ps:demo 是基于 kotlin 语言来写的,代码是基于 Android Api 26 剖析的

后面写了2篇的Android中查看事件散发的一些源码剖析,演示演示和总结一些论断,这一篇接着写 的Android中V IEW 事件配给物的论断。

查看的onTouchEvent默认不会超时事件,即它的返回值为false,咱们查看一下查看中的onTouchEvent办法源码:

public boolean onTouchEvent(MotionEvent event) {

    ......    if ((viewFlags & ENABLED_MASK) == DISABLED) {        if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {            setPressed(false);        }        mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;        // A disabled view that is clickable still consumes the touch        // events, it just doesn't respond to them.        return clickable;    }    if (mTouchDelegate != null) {        if (mTouchDelegate.onTouchEvent(event)) {            return true;        }    }    if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {        ......        return true;    }    return false;

}

看这个办法的后果,发现返回是假的,能够看到的onTouchEvent 办法默认返回值是假的,写一个演示验证一下咱们(案例D):

案例D

(1)新建一个 kotlin 语言的类 MyView 并继承于 View:

class MyView: View {

 constructor(context: Context): super(context) { }constructor(context: Context,@Nullable attrs: AttributeSet): super(context,attrs) {}constructor(context: Context,@Nullable attrs: AttributeSet,defStyleAttr: Int): super(context,attrs,defStyleAttr) {}override fun onTouchEvent(event: MotionEvent?): Boolean {    var b: Boolean = super.onTouchEvent(event)    Log.d(D_Activity.TAG,"MyView----" + b)    return b}

}

(2)新建一个 kotlin 类型的 Activity,名为 D_Activity 并继承于 AppCompatActivity:

class D_Activity : AppCompatActivity() {

companion object {    var TAG: String = "D_Activity"}override fun onCreate(savedInstanceState: Bundle?) {    super.onCreate(savedInstanceState)    setContentView(R.layout.activity_d_)}

}

(3)D_Activity对应的图文文件activity_d_如下所示:

<?xml version="1.0" encoding="utf-8"?>
<com.xe.views.MyView

xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:background="#FF0000"android:layout_height="match_parent">

</com.xe.views.MyView>

程序一开始运行的界面如下所示:

图片

用手触摸一下屏幕,打印如下:

07-27 13:23:07.878 19660-19660/com.xe.eventdemo3 D/D_Activity: MyView----false

View 的 onTouchEvent 办法默认返回验证了这值为 false。

如果有2个视图有重叠且父元素不拦挡事件,都要做向下解决的事件,也就是生产挪动、向上等事件,那么最下面的视图有可能接管到事件处理,被笼罩的视图就接管不到到事件;如果在下面的视图不生产所有事件,那么就在上面的视图生产蕴含的事件,那么在该视图上面的视图就会接管到事件处理。这有点不太好了解,咱们举个例子(案例E):

案例E

(1)新建一个 kotlin 语言的类 MyView2 并继承于 View:

class MyView2: View {

constructor(context: Context): super(context) {}constructor(context: Context, @Nullable attrs: AttributeSet): super(context,attrs) {}constructor(context: Context, @Nullable attrs: AttributeSet, defStyleAttr: Int): super(context,attrs,defStyleAttr) {}override fun onTouchEvent(event: MotionEvent?): Boolean {    when (event?.action) {        MotionEvent.ACTION_DOWN -> {            Log.d(E_Activity.TAG,"--MyView2--ACTION_DOWN")        }        MotionEvent.ACTION_MOVE -> {            Log.d(E_Activity.TAG,"--MyView2--ACTION_MOVE")        }        MotionEvent.ACTION_UP -> {            Log.d(E_Activity.TAG,"--MyView2--ACTION_UP")        }    }    return true}

}

(2)新建一个 kotlin 语言的类 MyView3 并继承于 View:

class MyView3: View {

constructor(context: Context): super(context) {}constructor(context: Context, @Nullable attrs: AttributeSet): super(context,attrs) {}constructor(context: Context, @Nullable attrs: AttributeSet, defStyleAttr: Int): super(context,attrs,defStyleAttr) {}override fun onTouchEvent(event: MotionEvent?): Boolean {    when (event?.action) {        MotionEvent.ACTION_DOWN -> {            Log.d(E_Activity.TAG,"--MyView3--ACTION_DOWN")        }        MotionEvent.ACTION_MOVE -> {            Log.d(E_Activity.TAG,"--MyView3--ACTION_MOVE")        }        MotionEvent.ACTION_UP -> {            Log.d(E_Activity.TAG,"--MyView3--ACTION_UP")        }    }    return true}

}

(3)新建一个kotlin类型的Activity,名叫E_Activity:

class E_Activity : AppCompatActivity() {

companion object {    var TAG: String = "E_Activity"}override fun onCreate(savedInstanceState: Bundle?) {    super.onCreate(savedInstanceState)    setContentView(R.layout.activity_e_)}

}

(4)E_Activity对应的布局文件activity_e_:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout

xmlns:android="http://schemas.android.com/apk/res/android"android:background="#00FF00"android:layout_width="match_parent"android:layout_height="match_parent"><com.xe.views.MyView2    android:layout_width="400px"    android:layout_height="400px"    android:background="#FF0000"></com.xe.views.MyView2><com.xe.views.MyView3    android:layout_width="200px"    android:layout_height="200px"    android:background="#0000FF"></com.xe.views.MyView3>

</FrameLayout>

程序一开始运行的界面如下所示:

图片

当咱们用手指触摸蓝色区域时,打印日志如下:

07-28 13:20:26.400 4554-4554/com.xe.eventdemo3 D/E_Activity: --MyView3--ACTION_DOWN
07-28 13:20:26.485 4554-4554/com.xe.eventdemo3 D/E_Activity: --MyView3--ACTION_MOVE
07-28 13:20:26.837 4554-4554/com.xe.eventdemo3 D/E_Activity: --MyView3--ACTION_MOVE
07-28 13:20:26.935 4554-4554/com.xe.eventdemo3 D/E_Activity: --MyView3--ACTION_UP

首先activity_e_.xml 这个文件的布局采纳的是FrameLayout,它的布局款式是,子视图如果不增加管制地位的属性,个别默认设置在FrameLayout的左上角,再增加的子视图后面增加的子视图给笼罩掉,也就是增加的子视图会看到后面增加子视图的下面;MyView2是一开始就增加的,背景色彩是红色,而MyView3是增加的,色彩是蓝色,MyView3会MyView2 的下面;从日志打印进去,MyView2 天经地义的没有接管到任何事件,而 MyView3 接管到了向下、挪动、向上事件。

咱们想改一下,把MyView3的onTouchEvent办法的返回值改为假,再运行程序日志,再用手指触摸蓝色的区域,打印如下所示:

07-28 13:48:19.467 9254-9254/com.xe.eventdemo3 D/E_Activity: --MyView3--ACTION_DOWN
07-28 13:48:19.468 9254-9254/com.xe.eventdemo3 D/E_Activity: --MyView2--ACTION_DOWN
07-28 13:48:19.644 9254-9254/com.xe.eventdemo3 D/E_Activity: --MyView2--ACTION_MOVE
07-28 13:48:19.828 9254-9254/com.xe.eventdemo3 D/E_Activity: --MyView2--ACTION_MOVE
07-28 13:48:19.845 9254-9254/com.xe.eventdemo3 D/E_Activity: --MyView2--ACTION_MOVE
07-28 13:48:19.878 9254-9254/com.xe.eventdemo3 D/E_Activity: --MyView2--ACTION_MOVE
07-28 13:48:19.895 9254-9254/com.xe.eventdemo3 D/E_Activity: --MyView2--ACTION_MOVE
07-28 13:48:19.962 9254-9254/com.xe.eventdemo3 D/E_Activity: --MyView2--ACTION_MOVE
07-28 13:48:20.122 9254-9254/com.xe.eventdemo3 D/E_Activity: --MyView2--ACTION_UP

从日志,MyView2 的TouchEvent 办法被调用了并进行了事件处理,是因为位于MyView2 之上的MyView3 没有处理事件,所以MyView2 的父元素引发事件给了MyView2。

在事件流传的过程中,如果子视图解决了事件(曾经解决了事件,就是 onTouchEvent 的返回值为 true),那么父元素 ViewGroup 的 onTouchEvent 办法一次都不会调用;如果子视图和父元素 ViewGroup 都没有处理事件,那么他们都会执行下来事件,而且还是子视图的返回如果优先执行,但它们的值依然是假的;子视图接管到了下来事件,没有生产,而父元素视图组进行了生产,那么子查看的羽绒还是比父元素的羽绒事件优先执行。

咱们先来看看ViewGroup的dispatchTouchEvent办法的一段源码:

public boolean dispatchTouchEvent(MotionEvent ev) {

    ......    final boolean intercepted;    if (actionMasked == MotionEvent.ACTION_DOWN            || mFirstTouchTarget != null) {        final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;        if (!disallowIntercept) {            intercepted = onInterceptTouchEvent(ev);            ev.setAction(action); // restore action in case it was changed        } else {            intercepted = false;        }    } else {        // There are no touch targets and this action is not an initial down        // so this view group continues to intercept touches.        intercepted = true;    }    ......

}

从ViewGroup的dispatchTouchEvent办法的办法的触发事件能够触发事件,触发事件是通过disallowIntercept变量和onInterceptTouchEvent办法管制的,触发事件无奈拦挡;传递过程是由内向内的即事件总是先传递给父元素ViewGroup,而后再由父元素ViewGroup流传给子View,子View通过getParent().requestDisallowInterceptTouchEvent(值参数) 属性能够在子元素中引入父元素的事件流传过程,下来事件除外。为了更好的了解,咱们来举个例子(案例F):

案例F

(1)新建一个 kotlin 的类 MyView4 并继承于 View(这里的 parent.requestDisallowInterceptTouchEvent(true) 语句等同于 Java 的 getParent().requestDisallowInterceptTouchEvent(true) 语句:

class MyView4: View {

constructor(context: Context): super(context) {}constructor(context: Context, @Nullable attrs: AttributeSet): super(context,attrs) {}constructor(context: Context, @Nullable attrs: AttributeSet, defStyleAttr: Int): super(context,attrs,defStyleAttr) {}override fun onTouchEvent(event: MotionEvent?): Boolean {    when (event?.action) {        MotionEvent.ACTION_DOWN -> {            Log.d(F_Activity.TAG,"--MyView4--onTouchEvent--ACTION_DOWN")            super.onTouchEvent(event)            parent.requestDisallowInterceptTouchEvent(true)        }        MotionEvent.ACTION_MOVE -> {            Log.d(F_Activity.TAG,"--MyView4--onTouchEvent--ACTION_MOVE")        }        MotionEvent.ACTION_UP -> {            Log.d(F_Activity.TAG,"--MyView4--onTouchEvent--ACTION_UP")        }    }    return true}

}

(2)新建一个 kotlin 语言的类 MyFrameLayout 并继承于 FrameLayout:

class MyFrameLayout: FrameLayout {

constructor(context: Context): super(context) {}constructor(context: Context, @Nullable attrs: AttributeSet): super(context,attrs) {}constructor(context: Context, @Nullable attrs: AttributeSet, defStyleAttr: Int): super(context,attrs,defStyleAttr) {}override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {    when (ev?.action) {        MotionEvent.ACTION_DOWN -> {            Log.d(F_Activity.TAG,"--MyFrameLayout--onInterceptTouchEvent--ACTION_DOWN")            super.onInterceptTouchEvent(ev)            return false        }        MotionEvent.ACTION_MOVE -> {            Log.d(F_Activity.TAG,"--MyFrameLayout--onInterceptTouchEvent--ACTION_MOVE")        }        MotionEvent.ACTION_UP -> {            Log.d(F_Activity.TAG,"--MyFrameLayout--onInterceptTouchEvent--ACTION_UP")        }    }    return true}

}

(3)新建一个kotlin语言的Activity,名为F_Activity并继承于AppCompatActivity:

class F_Activity : AppCompatActivity() {

companion object {    var TAG: String = "F_Activity"}override fun onCreate(savedInstanceState: Bundle?) {    super.onCreate(savedInstanceState)    setContentView(R.layout.activity_f_)}

}

(4)F_Activity对应的布局文件activity_f_.xml如下所示:

<?xml version="1.0" encoding="utf-8"?>
<com.xe.views.MyFrameLayout

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:background="#FF0000"tools:context="com.xe.eventdemo3.F_Activity"><com.xe.views.MyView4    android:layout_width="match_parent"    android:layout_height="300px"    android:background="#0000FF"/>

</com.xe.views.MyFrameLayout>

程序一开始运行的界面如下所示:

图片

当咱们用手触摸蓝色区域的时候,打印日志如下所示:

07-30 13:33:33.619 27770-27770/? D/F_Activity: --MyFrameLayout--onInterceptTouchEvent--ACTION_DOWN
07-30 13:33:33.620 27770-27770/? D/F_Activity: --MyView4--onTouchEvent--ACTION_DOWN
07-30 13:33:33.696 27770-27770/? D/F_Activity: --MyView4--onTouchEvent--ACTION_MOVE
07-30 13:33:33.764 27770-27770/? D/F_Activity: --MyView4--onTouchEvent--ACTION_MOVE
07-30 13:33:33.780 27770-27770/? D/F_Activity: --MyView4--onTouchEvent--ACTION_MOVE
07-30 13:33:33.974 27770-27770/? D/F_Activity: --MyView4--onTouchEvent--ACTION_MOVE
07-30 13:33:33.975 27770-27770/? D/F_Activity: --MyView4--onTouchEvent--ACTION_UP

从录音,MyView4申请MyFrameLayout不拦挡事件胜利,咱们联合下面ViewGroup的dispatchTouchEvent办法源码进行剖析;

final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
if (!disallowIntercept) {

intercepted = onInterceptTouchEvent(ev);ev.setAction(action); // restore action in case it was changed

} else {

intercepted = false;

}

当 MyFrameLayout 的 onInterceptTouchEvent 不对向下事件进行拦挡时办法,MyView4 接管到向下事件并调用 parent.requestDisallowInterceptTouchEvent(true) 语句让第二次调用 ViewGroup 的 dispatchTouchEvent 办法中的 disallowIntercept 为 true,从而让 MyFrameLayout 的 onInterceptTouchEvent 无奈再次调用,所以也让截取的值为假。

咱们把parent.requestDisInterceptTouchEvent(true)语句的参数改为false 再运行程序,用手指再触摸蓝色的区域,打印日志:

07-30 13:55:01.733 31063-31063/? D/F_Activity: --MyFrameLayout--onInterceptTouchEvent--ACTION_DOWN
07-30 13:55:01.733 31063-31063/? D/F_Activity: --MyView4--onTouchEvent--ACTION_DOWN
07-30 13:55:01.786 31063-31063/? D/F_Activity: --MyFrameLayout--onInterceptTouchEvent--ACTION_MOVE

这里的parent.DisallowInterceptTouchEvent(false)语句的参数为false和不写该语句的成果是的,都是申请办法了ViewGroup的dispatchTouchEvent中的disallowIntercept 是false,手指次挪动的过程中,加上第二调用MyFrameLayout 的 onInterceptTouchEvent 办法并且它的返回值为 true,也就是 move 事件的返回值为 true,所以 MyView4 的 move 事件被拦挡了,所以 MyView4 的 move 事件没有打印日志。