乐趣区

关于android:Android中查看事件的发布第二篇

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

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? = null
override 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

图片

退出移动版