1.让我们一起来聊聊Android事件分发机制,废话少说直接进入正题;首先我们重写一个ViewGroup(此处以LinearLayout为例)和View(此处以TextView为例)的事件分发三个方法(View只有两个方法)如下:
dispatchTouchEvent()负责分发事件
onInterceptTouchEvent()负责拦截事件
onTouchEvent()消费事件
class MyLinearLayout @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
Log.e("===","MyLinearLayout dispatchTouchEvent = ${ev?.action}")
return super.dispatchTouchEvent(ev)
}
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
Log.e("===","MyLinearLayout onInterceptTouchEvent = ${ev?.action}")
return super.onInterceptTouchEvent(ev)
}
override fun onTouchEvent(ev: MotionEvent?): Boolean {
Log.e("===","MyLinearLayout onTouchEvent = ${ev?.action}")
return super.onTouchEvent(ev)
}
}
class MyTextView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : TextView(context, attrs, defStyleAttr) {
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
Log.e("===","MyTextView dispatchTouchEvent = ${ev?.action}")
return super.dispatchTouchEvent(ev)
}
override fun onTouchEvent(ev: MotionEvent?): Boolean {
Log.e("===","MyTextView onTouchEvent = ${ev?.action} ")
return super.onTouchEvent(ev)
}
}
2.我们在activity_main.xml布局文件添加我们重写的两个View如下:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<wxl.com.touchdemo.MyLinearLayout
android:id="@+id/lltParent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="40dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:gravity="center"
android:background="#ff0"
app:layout_constraintTop_toTopOf="parent">
<wxl.com.touchdemo.MyTextView
android:id="@+id/tvChild"
android:layout_width="150dp"
android:layout_height="100dp"
android:gravity="center"
android:background="#ff0000"
android:text="子View"/>
</wxl.com.touchdemo.MyLinearLayout>
</android.support.constraint.ConstraintLayout>
3.在MainActivity代码中设置一个MyTextView的onClick(点击事件),如下:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initView()
}
private fun initView() {
tvChild.setOnClickListener {
Log.e("===","子View")
}
}
override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
Log.e("===", "MainActivity dispatchTouchEvent = ${ev?.action}")
return super.dispatchTouchEvent(ev)
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
Log.e("===","MainActivity onTouchEvent = ${event?.action}")
return super.onTouchEvent(event)
}
}
4.到这里我们可以跑起来了如下图; 我们没对事件进行任何处理,仅仅重写了它的方法;当点击子View时,来看看运行结果(附图片)
根据以上的运行结果做下总结:当我们点击子View时(相当于子View的onTouchEvent()消费了该次事件),首先执行的是ACTION_DOWN(ACTION_DOWN=0以下称down事件)事件,因为我们给子View做了监听所以子View消费了该次事件,即子View的onTouchEvent()方法被执行返回true, down事件结束;ACTION_MOVE(ACTION_MOVE=2以下称move事件)和ACTION_UP(ACTION_UP=1以下称up事件)事件就会发送;当滑动时move事件会重复执行多次,事件都会在子View的onTouchEvent()方法返回true时被消费;下图为move事件运行结果:
5.当MyLinearLayout的onInterceptTouchEvent()返回true时来看看运行结果:
事件被MyLinearLayout拦截后会走MyLinearLayout的onTouchEvent(),但是并没有被onTouchEvent()消费掉回传给了Activity的onTouchEvent()事件消失,move和up事件就只会在MainActivity中进行传递并消失;(move事件比较多此处就不再结果中体现了)
6.当MyLinearLayout的dispatchTouchEvent()返回true时来看看运行结果:
事件在MyLinearLayout的dispatchTouchEvent()返回true表示事件不分发了,事件传到dispatchTouchEvent()方法就结束没有往下传递了(上图刚好点出来一个move事件^-^),说到这里可能有人会疑惑返回true 不是会分发事件吗?其实事件分发的核心在super.dispatchTouchEvent()方法中,此方法的返回结果也true; 所以当我们单独返回true是事件并不会被分发;那返回false呢?我们来看返回false的运行结果:
所以我们要让事件正常分发返回super.dispatchTouchEvent()方法就行;具体原因自己去看了^-^
7.当MyTextVeiw的dispatchTouchEvent()返回true时来看看运行结果:
结果跟上面基本一样;
上面可能比较乱总结一下:1.当down事件传递到子View 的onTouchEvent()没有被消费掉, 就会传递给上级的onTouchEvent() ,如果一直没有被消费就会逐级往上传递最终传递给顶级父类最后消失,后续的move和up事件只会传递到down最后消失的父类中不会往下传递给子类;
2.当down事件传递过程中被拦截,down事件不会再往下传递,而是传递到当前拦截事件的View的onTouchEvent();如果事件没有被当前View消费掉,也会像上面所诉传递给上级的onTouchEvent(),这样逐级上传知道被消费或消失,后面同上所诉;