浅谈android事件分发机制
最近准备换项目,因此需要重新回顾一下一些知识点,其中就包含事件分发机制,
先说说什么是事件分发机制,简单点说事件分发机制就是“将点击事件(MotionEvent)传递到某个具体的View
& 处理的整个过程“,注意”过程“这俩个字
看别人的再多东西,不如自己上手来一遍,写个简单的demo然后我们来学习下事件分发机制。以下是代码准备
import android.os.Bundle;
import android.util.Log;
import android.view.MotionEvent;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e("ssssssssssEvent","Activity:dispatchTouchEvent");
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("ssssssssssEvent","Activity:onTouchEvent");
return false;
}
}
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.LinearLayout;
import androidx.annotation.Nullable;
public class MyLinearLayout extends LinearLayout {
public MyLinearLayout(Context context) {
super(context);
}
public MyLinearLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public MyLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e("ssssssssssEvent","MyLinearLayout:dispatchTouchEvent");
return super.dispatchTouchEvent(ev);
// return true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("ssssssssssEvent","MyLinearLayout:onTouchEvent");
return false;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e("ssssssssssEvent","MyLinearLayout:onInterceptTouchEvent");
return false;
}
}
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.Button;
public class MyButton extends Button {
public MyButton(Context context) {
super(context);
}
public MyButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
Log.e("ssssssssssEvent","MyButton:dispatchTouchEvent");
return super.dispatchTouchEvent(event);
// return true;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
Log.e("ssssssssssEvent","MyButton:onTouchEvent");
return false;
}
}
<?xml version="1.0" encoding="utf-8"?>
<com.example.myapplication.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:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<com.example.myapplication.MyButton
android:layout_width="match_parent"
android:layout_height="50dp"
android:id="@+id/btn_btn"
android:text="按钮" />
</com.example.myapplication.MyLinearLayout>
代码呢就这么多,运行一下观察log,
不难看出,所有都按照正常流程走的话,事件会先从Activity 的dispatchTouchEvent开始向下分发,然后走到Viewgroup的dispatchTouchEvent方法中,然后重点来了"onInterceptTouchEvent"这个方法是做什么的呢,我们先留个悬念,接着往下走,然后事件分发到View的dispatchTouchEvent方法中,到这里dispatchTouchEvent向下分发就结束了,然后开始一层一层执行onTouchEvent方法,最终走到Activity的onTouchEvent方法中,这个是正常情况下的事件分发机制。
接着我们修改一下Activity中的dispatchTouchEvent,修改成下面这样子再观察log
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e("ssssssssssEvent","Activity:dispatchTouchEvent");
// return super.dispatchTouchEvent(ev);
return true;
}
很明显如果吧activity中的dispatchTouchEvent的返回值改为true,事件就不向下传递了,说明此次事件被activity自己消费了。
我们再来接着改下这几个地方,先把activity 的代码改回去
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e("ssssssssssEvent","Activity:dispatchTouchEvent");
return super.dispatchTouchEvent(ev);
}
接着再改下viewgroup的dispatchTouchEvent代码,运行观察log,此时我们可以猜想一下,是不是結果应该是Activity的dispatchTouchEvent向下分发到ViewGroup的dispatchTouchEvent方法中,就停住了不再执行其他任何方法了?
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e("ssssssssssEvent","MyLinearLayout:dispatchTouchEvent");
// return super.dispatchTouchEvent(ev);
return true;
}
結果果然和我们猜想的是一样的。
此时我们就可以得到一个结论,dispatchTouchEvent方法的作用就是事件是否被消费,一旦被消费事件分发就到此而止了。
弄清楚了dispatchTouchEvent方法,还有两个函数onInterceptTouchEvent和onTouchEvent方法,别急我们接下来就来看这个两个个方法的作用
我们现在吧viewgroup的dispatchTouchEvent还原回去
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
Log.e("ssssssssssEvent","MyLinearLayout:dispatchTouchEvent");
return super.dispatchTouchEvent(ev);
}
再修改viewgroup的如下代码,并运行观察log
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
Log.e("ssssssssssEvent","MyLinearLayout:onInterceptTouchEvent");
// return false;
return true;
}
我们可以看到这个时候不仅执行了onInterceptTouchEvent方法,而且还执行了viewgroup和activity的onTouchEvent方法,但是却没有执行的view的这两个方法,是不是我们就可以得出一个结论,onInterceptTouchEvent如果返回true的话,那么代表着整个事件分发机制被viewgroup响应,而不会再向下分发,因此onInterceptTouchEvent的作用就相当于一个拦截器,拦截住给自己使用。
好像忘了一个事情,就是dispatchTouchEvent如果返回false呢,会是一个什么结果呢,为什么再这个时候提这个问题呢,我们再来试试
再对viewgroup做出如下修改,运行查看log:
@Override public boolean dispatchTouchEvent(MotionEvent ev) { Log.e("ssssssssssEvent","MyLinearLayout:dispatchTouchEvent"); // return super.dispatchTouchEvent(ev); return false; }
結果为什么和onInterceptTouchEvent好像,但是并没有执行onInterceptTouchEvent这个方法,因为当dispatchTouchEvent返回为false的时候,事件分发流程就会被当前的层给相应,因此onInterceptTouchEvent的作用也就失去了。所以dispatchTouchEvent的作用域要大于onInterceptTouchEvent的。
就剩下最后一个onTouchEvent方法了,这个方法就很面熟了,在我们日常写代码的时候用的也比较多了,其实这个方法中就是我们对一些触摸事件的处理,如果实在不知道可自行百度。
到这里我们也就结束了,是不是自己动手操作一遍发现原理其实就这么简单,原理是这样但是实际应用中其实摒弃掉其他东西也就是这么个事情,最后在总结一下:
什么是事件分发机制-将触摸事件(滑动,点击,长按不知道还有其他没)传递到某个View或者Viewgroup的这个过程
事件分发三个方法的介绍以及各自的作用
dispatchTouchEvent (activity,viewgroup,view都有的一个方法),判断此次行为是否被消费,一旦返回true那么剩下所有流程将都会停止,返回false的话,就会在当前层上相应行为,也不会向下传递,并且viewgroup中一旦返回false将不再执行onInterceptTouchEvent方法,此处需要注意
onInterceptTouchEvent(viewgroup中独有的方法),是否拦截行为,一旦返回true那么此次行为将有自己使用,不再向下传递
onTouchEvent(activity,viewgroup,view都有的一个方法),事件分发的具体实现了,到了这里就是你想用这个事件干什么了。