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

ps:源码是基于android api 26来剖析的,demo是用kotlin语言写的

后面写了一篇中查看事件的流传第一篇对事件流传的一些源码进行剖析以及写进行演示论断;这一篇验证文章还对事件的流传进一步的学习与摸索。

一个事件序列是指从手指触摸屏幕的学习起,到成长过程中成长的成长过程完结,这一过程中会产生回升的完结,事件最终以事件完结,中会途完结有N个挪动事件;在Android中查看事件的流传第一篇这篇文章中演示过,所以这里就不再进行演示了。

个别状况下,一个事件序列可能被-个视图拦挡并解决,一个事件序列中的事件是由一个视图解决的,一个视图元素如果拦挡了下来事件,而后一个事件序列内的挪动等所有事件都间接到这个视图解决,而且这个视图的onInterceptTouchEvent办法被举了一次。上面咱们个例子(案例A);

案例A

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

class MyView : View {

constructor(context: Context) : super(context) {}constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {}constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {}override fun onTouchEvent(event: MotionEvent?): Boolean {    Log.d(A_Activity.TAG, "-----MyView--onTouchEvent")    return false}

}

(2)新建一个kt语言的类MyLinearLayout并继承于 LinearLayout:

class MyLinearLayout: LinearLayout {

constructor(context: Context): super(context) {}constructor(context: Context,attrs: AttributeSet): super(context,attrs) {}constructor(context: Context,attrs: AttributeSet,defStyleAttr: Int): super(context,attrs,defStyleAttr) {}override fun onTouchEvent(event: MotionEvent?): Boolean {    when(event?.action) {        MotionEvent.ACTION_DOWN -> {            Log.d(A_Activity.TAG,"-----MyLinearLayout--onTouchEvent--ACTION_DOWN")        }        MotionEvent.ACTION_MOVE -> {            Log.d(A_Activity.TAG,"-----MyLinearLayout--onTouchEvent--ACTION_MOVE")        }        MotionEvent.ACTION_UP -> {            Log.d(A_Activity.TAG,"-----MyLinearLayout--onTouchEvent--ACTION_UP")        }    }    return true}override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {    if (ev?.action == MotionEvent.ACTION_DOWN) {        Log.d(A_Activity.TAG,"-----MyLinearLayout----onInterceptTouchEvent")        return true    } else if (ev?.action == MotionEvent.ACTION_MOVE) {        Log.d(A_Activity.TAG,"-----MyLinearLayout----onInterceptTouchEvent")    }    return false}

}

(3)新建一个kt语言类型的Activity,名字叫A_Activity:

class A_Activity : AppCompatActivity() {

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

}

(4)A_Activity对应的布局文件为activity_a_.xml:

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

xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:orientation="vertical"android:background="#FF0000"android:layout_height="match_parent"tools:context="com.xe.eventdemo2.A_Activity"><com.xe.views.MyView    android:layout_width="match_parent"    android:layout_height="200px"    android:background="#00FF00"></com.xe.views.MyView>

</com.xe.views.MyLinearLayout>

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

图片

当咱们点击绿色的区域时,便打印如下:

07-21 09:28:53.559 11572-11572/com.xe.eventdemo2 D/A_Activity: -----MyLinearLayout----onInterceptTouchEvent
07-21 09:28:53.560 11572-11572/com.xe.eventdemo2 D/A_Activity: -----MyLinearLayout--onTouchEvent--ACTION_DOWN

07-21 09:28:53.788 11572-11572/com.xe.eventdemo2 D/A_Activity: -----MyLinearLayout--onTouchEvent--ACTION_MOVE
07-21 09:28:53.804 11572-11572/com.xe.eventdemo2 D/A_Activity: -----MyLinearLayout--onTouchEvent--ACTION_MOVE
07-21 09:28:53.821 11572-11572/com.xe.eventdemo2 D/A_Activity: -----MyLinearLayout--onTouchEvent--ACTION_MOVE
07-21 09:28:54.021 11572-11572/com.xe.eventdemo2 D/A_Activity: -----MyLinearLayout--onTouchEvent--ACTION_MOVE
07-21 09:28:54.048 11572-11572/com.xe.eventdemo2 D/A_Activity: -----MyLinearLayout--onTouchEvent--ACTION_UP

这时候发现 MyView 的 onTouchEvent 办法没有被调用,在这个案例中,MyView 子元素,MyLinearLayout 成为父元素,MyLinearLayout重写 onInterceptTouchEvent 并在事件中返回了实在的就相当于对子元素 MyView的事件进行拦挡并应该办法只调用一次。

如果看到不生产除掉以外的其余事件,父元素生产事件除掉以外的其余事件,那么点击事件就会隐没,这些隐没的点击事件最终会传递给流动解决。好,上面咱们来写一个demo来验证下面的这句话(案例B);

案例B

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

class MyView2 : View {

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

}

(2)新建一个kt语言的类MyLinearLayout2并继承于 LinearLayout:

class MyLinearLayout2: LinearLayout {

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

}

(3)新建一个kt语言类型的Activity,名字叫B_Activity:

class B_Activity : AppCompatActivity() {

companion object {    var TAG: String = "B_Activity"}override fun onCreate(savedInstanceState: Bundle?) {    super.onCreate(savedInstanceState)    setContentView(R.layout.activity_b_)}override fun onTouchEvent(event: MotionEvent?): Boolean {    when(event?.action) {        MotionEvent.ACTION_DOWN -> {            Log.d(TAG,"-----B_Activity--onTouchEvent--ACTION_DOWN")        }        MotionEvent.ACTION_MOVE -> {            Log.d(TAG,"-----B_Activity--onTouchEvent--ACTION_MOVE")        }        MotionEvent.ACTION_UP -> {            Log.d(TAG,"-----B_Activity--onTouchEvent--ACTION_UP")        }    }    return super.onTouchEvent(event)}

}

(4)B_Activity对应的布局文件为activity_b_.xml:

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

xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:orientation="vertical"android:background="#FF0000"android:layout_height="match_parent"tools:context="com.xe.eventdemo2.A_Activity"><com.xe.views.MyView2    android:layout_width="match_parent"    android:layout_height="200px"    android:background="#00FF00"></com.xe.views.MyView2>

</com.xe.views.MyLinearLayout2>

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

图片

而后咱们用手指手指的区域,打印日志如下:

07-22 13:23:52.375 26535-26535/com.xe.eventdemo2 D/B_Activity: -----MyView2--onTouchEvent--ACTION_DOWN
07-22 13:23:52.375 26535-26535/com.xe.eventdemo2 D/B_Activity: -----MyLinearLayout2--onTouchEvent--ACTION_DOWN
07-22 13:23:52.376 26535-26535/com.xe.eventdemo2 D/B_Activity: -----B_Activity--onTouchEvent--ACTION_DOWN
07-22 13:23:52.503 26535-26535/com.xe.eventdemo2 D/B_Activity: -----B_Activity--onTouchEvent--ACTION_MOVE
07-22 13:23:52.686 26535-26535/com.xe.eventdemo2 D/B_Activity: -----B_Activity--onTouchEvent--ACTION_MOVE
07-22 13:23:52.703 26535-26535/com.xe.eventdemo2 D/B_Activity: -----B_Activity--onTouchEvent--ACTION_MOVE
07-22 13:23:52.820 26535-26535/com.xe.eventdemo2 D/B_Activity: -----B_Activity--onTouchEvent--ACTION_MOVE
07-22 13:23:52.853 26535-26535/com.xe.eventdemo2 D/B_Activity: -----B_Activity--onTouchEvent--ACTION_UP

下面的图片总结的一样,案例中的 MyView2 和真的父 MyLinearLayout2 的触摸事件只消消了事件时,其余事件事件咱们来解决。

ViewGroup中默认是不拦挡事件的,也就是ViewGroup中的onInterceptTouch-事件办法返回值是假的。咱们来看看一个ViewGroup的 onInterceptTouchEv连贯牛逼方行业释义法律;

public boolean onInterceptTouchEvent(MotionEvent ev) {

    if (ev.isFromSource(InputDevice.SOURCE_MOUSE)            && ev.getAction() == MotionEvent.ACTION_DOWN            && ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)            && isOnScrollbarThumb(ev.getX(), ev.getY())) {        return true;    }    return false;}

发现该办法返回的默认值是假的,只有在事件源类型是鼠标并且是向下的事件是单击按钮和是滚动条的典型时才返回真。所以个别的 ViewGroup 的 onInterceptTouchEvent 办法返回都为假,也就是说默认不拦挡事件。

查看没有onInterceptTouchEvent办法,如果有事件传递给它,也就是dispatchTouchEvent办法被调用,那么它的的onTouchEvent办法就会被调用(如果没有onTouch办法,或者onTouch办法返回值为假的条件下)。咱们来看看查看的 dispatchTouchEvent 办法;

public boolean dispatchTouchEvent(MotionEvent event) {

    ......    //noinspection SimplifiableIfStatement    ListenerInfo li = mListenerInfo;    if (li != null && li.mOnTouchListener != null            && (mViewFlags & ENABLED_MASK) == ENABLED            && li.mOnTouchListener.onTouch(this, event)) {        result = true;    }    if (!result && onTouchEvent(event)) {        result = true;    }    ......    return result;

}

发现 View 的 dispatchTouchEvent 办法有调用 onTouchEvent 办法。

View 的 onClick 办法会执行的前提是以后 View 是可点击的(也就是 clickable 和 enabled 属性只有有一个为真),并且它收到了 down 和 up 的事件;如果重写了 View 的 onTouchEvent 办法,那么 down和up事件的返回值必须为super.onTouchEvent(event),才会调用View的onClick办法,具体起因看Android中View事件的第一篇,上面咱们写一个demo来验证能够点论断(案例) C);

案例C

(1)新建一个kt语言的类MyView3并继承与View:

class MyView3: View {

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

}

(2)新建一个kt语言类型的Activity,名字叫C_Activity:

class C_Activity : AppCompatActivity() {

companion object {    var TAG: String = "C_Activity";}var mMyView3: MyView3? = nulloverride fun onCreate(savedInstanceState: Bundle?) {    super.onCreate(savedInstanceState)    setContentView(R.layout.activity_c_)    mMyView3 = findViewById(R.id.my_view3);    mMyView3!!.isEnabled = true    mMyView3!!.isClickable = false    mMyView3!!.setOnClickListener(object : View.OnClickListener {        override fun onClick(v: View?) {            Log.d(TAG,"--C_Activity *** onClick--")        }    })}

}

(3)C_Activity对应的布局文件为activity_c_.xml:

<?xml version="1.0" encoding="utf-8"?>
<com.xe.views.MyView3 xmlns:android="http://schemas.android.com/apk/res/android"

android:id="@+id/my_view3"android:layout_width="match_parent"android:background="#FF0000"android:layout_height="match_parent">

</com.xe.views.MyView3>

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

图片

当咱们用手指办法时触摸,日志打印如下所示,能够看到点击被调用了:

07-26 08:58:33.206 5056-5056/com.xe.eventdemo2 D/C_Activity: -----MyView3--onTouchEvent--ACTION_DOWN
07-26 08:58:33.303 5056-5056/com.xe.eventdemo2 D/C_Activity: -----MyView3--onTouchEvent--ACTION_MOVE
07-26 08:58:33.620 5056-5056/com.xe.eventdemo2 D/C_Activity: -----MyView3--onTouchEvent--ACTION_MOVE
07-26 08:58:33.637 5056-5056/com.xe.eventdemo2 D/C_Activity: -----MyView3--onTouchEvent--ACTION_MOVE
07-26 08:58:33.670 5056-5056/com.xe.eventdemo2 D/C_Activity: -----MyView3--onTouchEvent--ACTION_MOVE
07-26 08:58:33.754 5056-5056/com.xe.eventdemo2 D/C_Activity: -----MyView3--onTouchEvent--ACTION_UP
07-26 08:58:33.758 5056-5056/com.xe.eventdemo2 D/C_Activity: --C_Activity * onClick--

当咱们把 mMyView3 都改一下,也就是 mMyView3!!.isEnabled = false,mMyView3!!.isClickable = false,而后在运行程序用手触摸屏幕,日志打印如下所示,发现 onClick 办法没有被调用:

07-26 09:03:16.440 5856-5856/com.xe.eventdemo2 D/C_Activity: -----MyView3--onTouchEvent--ACTION_DOWN
07-26 09:03:16.525 5856-5856/com.xe.eventdemo2 D/C_Activity: -----MyView3--onTouchEvent--ACTION_MOVE
07-26 09:03:16.776 5856-5856/com.xe.eventdemo2 D/C_Activity: -----MyView3--onTouchEvent--ACTION_MOVE
07-26 09:03:16.851 5856-5856/com.xe.eventdemo2 D/C_Activity: -----MyView3--onTouchEvent--ACTION_UP

当咱们把mMyView3都改一下,也就是mMyView3!!.isEnabled = true,mMyView3!!.isClickable = false,而后再改一下MyView3的down事件的返回值为true,代码如下批改:

override fun onTouchEvent(event: MotionEvent?): Boolean {

    when(event?.action) {        MotionEvent.ACTION_DOWN -> {            Log.d(C_Activity.TAG,"-----MyView3--onTouchEvent--ACTION_DOWN")            return true        }        MotionEvent.ACTION_MOVE -> {            Log.d(C_Activity.TAG,"-----MyView3--onTouchEvent--ACTION_MOVE")            return true        }        MotionEvent.ACTION_UP -> {            Log.d(C_Activity.TAG,"-----MyView3--onTouchEvent--ACTION_UP")        }    }    return super.onTouchEvent(event)

}

程序运行后,用手发现屏幕,日志打印如下,也是没有调用 onClick 办法:

07-26 09:07:43.443 6687-6687/com.xe.eventdemo2 D/C_Activity: -----MyView3--onTouchEvent--ACTION_DOWN
07-26 09:07:43.574 6687-6687/com.xe.eventdemo2 D/C_Activity: -----MyView3--onTouchEvent--ACTION_MOVE
07-26 09:07:43.894 6687-6687/com.xe.eventdemo2 D/C_Activity: -----MyView3--onTouchEvent--ACTION_MOVE
07-26 09:07:43.896 6687-6687/com.xe.eventdemo2 D/C_Activity: -----MyView3--onTouchEvent--ACTION_UP

图片