android事件分发机制

写在前面:看了《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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值