android事件分发机制

来源:互联网 发布:windows kill进程命令 编辑:程序博客网 时间:2024/06/08 18:35

写在前面:看了《android群英传》的事件分发后,结合自己的一些理解,整理了一下,记录如下:

首先,在我的前面博客自定义view解析中有讲到view的结构树,它是由viewgroup和view相互嵌套组成的树形结构,一个viewgroup里可以放view也可以放其他viewgroup,然后逐层分发,那么当点击事件发生的时候,是由viewgroup执行呢?还是view执行?因此这里就产生了“事件拦截这一说法”。

然后,事件的分发和拦截的核心函数:
①dispatchTouchEvent
②onInterceptTouchEvent
③onTouchEvent
顾名思义,第一个是分发触摸事件,第二个是拦截触摸事件,第三个是处理触摸事件。

注意:只有ViewGroup类里才能重写onInterceptTouchEvent

顺序:(经过我个人的研究发现)事件最开始获取是在ViewGroup所处的Activity里得到,然后经过层层dispatch到达最小的view,再处理事件逻辑。

这里写图片描述

下面我给出代码验证一波:

public class MainActivity extends Activity {    private MyLinearLayout m1;    private MyButton m2;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        m1 = new MyLinearLayout(this);        m2 = new MyButton(this);        m2.setText("点我!");        setContentView(m1);        m1.addView(m2);    }    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        Log.i("test", "MainActivity dispatchTouchEvent");        return super.dispatchTouchEvent(ev);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        Log.i("test", "MainActivity onTouchEvent");        return super.onTouchEvent(event);    }}class MyLinearLayout extends LinearLayout {    public MyLinearLayout(Context context) {        super(context);    }    public MyLinearLayout(Context context, AttributeSet attrs) {        super(context, attrs);    }    public MyLinearLayout(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    @Override    public boolean dispatchTouchEvent(MotionEvent ev) {        Log.i("test", "MyLinearLayout dispatchTouchEvent");        return super.dispatchTouchEvent(ev);    }    @Override    public boolean onInterceptTouchEvent(MotionEvent ev) {        Log.i("test", "MyLinearLayout onInterceptTouchEvent");        return super.onInterceptTouchEvent(ev);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        Log.i("test", "MyLinearLayout onTouchEvent");        return super.onTouchEvent(event);    }}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.i("test", "MyButton dispatchTouchEvent");        return super.dispatchTouchEvent(event);    }    @Override    public boolean onTouchEvent(MotionEvent event) {        Log.i("test", "MyButton onTouchEvent");        return super.onTouchEvent(event);    }}

代码很简单,就是一个Acitivity里面两个自定义控件,然后各自重写了方法,并打上log,下面是log:

这里写图片描述

这里就可以清楚的看见最先拿到事件的正是MainActivity,然后dispatch给LinearLayout,在传给button。这里处理了两个事件一个是按下一个是抬起。

但是这里肯定有人不明白,为什么在LinearLayout和Activity里都有onTouchEvent但是他们的log并没有打出来,这是为什么?其实,事件从产生到消费(处理)是经过了两次传递的。从事件的产生获取,然后传递叫事件传递,传到最后被一个view处理了,再返回处理结果,是处理顺序,假设Activity为A对象,LinearLayout为B对象,Button为C对象,那么事件分发顺序为:A——>B——>C,而事件的处理顺序正好反过来,应该是C——>B——>A。如下图:

这里写图片描述

上面类中重写的方法默认返回值分发过程是false,而处理过程默认为true;分发事件时返回值改为true表示拦截事件,不再往下传递;处理事件返回值改为false表示不处理了交给上级处理。而dispatchTouchEvent是分发的第一步一般不去修改。

下面做三个实验,把LinearLayout的onInterceptTouchEvent方法返回值改为true;把button的onTouchEvent返回值改为false;上面一步的基础上再把LinearLayout的onTouchEvent返回值改为false。分别看下log

这里写图片描述

这里写图片描述

这里写图片描述

正如上文所述,被LinearLayout拦截后,Button不会收到事件,LinearLayout自己处理事件,后面抬起事件就直接传给LinearLayout处理不进行拦截和传递操作。然后后面两个修改onTouchEvent也同理只是处理时的位置不同,道理就是最小的view不处理就往上抛,直到有onTouchEvent返回为true的地方再进行处理。

ok,码了半天。事件分发应该差不多就这些东西,欢迎指正~

0 0
原创粉丝点击