Android View的事件分发

  • View的事件分发机制
    • 分析的对象
      • MotionEvent: 表示点击事件, 例如Action_DOWN、Action_UP、Action_MOVE等
    • 事件的传递过程
      • dispatchTouchEvent: 事件分发 (事件能否传递给当前View)
      • onInterceptTouchEvent : 事件拦截(是否拦截事件)
      • onTouchEvent : 处理点击事件

1、 产生一个Action_Down时,即产生了一个点击操作,事件由此开始,其顺序为 Activity -> Window -> View。最开始事件会在Activity的dispatchTouchEvent中进行事件的分发,实际的工作由Activity内部的Window完成。Window是一个抽象类,其唯一实现是PhoneWindow。在PhoneWindow中有一个内部类DecorView,而DecorView可以看成是我们当前界面的底层容器(也是我们setContentView设置的View的父容器)。在这,我们可以发现,事件会经过PhoneWindow -> DecorView传递到了顶级View,即setContentView设置的View。

下面看一张草图,以简单的形式描绘了一下大致的一个传递过程,包含了Activity层,ViewGroup层,View层。ViewGroup层是setContentView设置的View,View层是ViewGroup中的子View。

这里写图片描述

来捋一下上图中大致的流程,图中return super为父类的实现;false表示表示不消费事件;true表示消费事件。
《1》若在Activity层中的dispatchTouchEvent就return true(false),事件将不会再往下传递。
《2》事件传递到ViewGroup层中的dispatchTouchEvent时,若返回false,则事件会回传到父容器Activity层的onTouchEvent中消费。
反之,会传递到onInterceptTouchEvent,其默认为false,即不拦截事件,事件会传递给View层中的dispatchTouchEvent。若返回true时,表示拦截当前事件,直接会在ViewGroup层中的onTouchEvent决定是否消费此事件,返回true为消费,否者不消费,事件回传到父容器中的onTouchEvent。
《3》若View层中的dispatchTouchEvent可以接收事件,则由此再事件分发,若返回false,事件会回传到ViewGroup层中的onTouchEvent决定是否消费,这里再不消费,就会再回传到Activity层中的onTouchEvent消费。反之,再当前View层中消费事件。

注: onInterceptTouchEvent方法只属于ViewGroup。

2、 看一个简单的Demo,自定义两个布局文件,CustomViewR继承于RelativeLayout,CustomViewButton继承于AppCompatButton。实现相应的dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent。并且也在MainActivity重写dispatchTouchEvent和onTouchEvent方法。

MainActivity表示Acitvity层

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "MainActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, "dispatchTouchEvent: ->"+"Activity dispatch DOWN" );
                break;
        }
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, "onTouchEvent: ->"+"Activity onTouchEvent DOWN" );
                break;
        }
        return super.onTouchEvent(event);
    }
}

CustomViewR表示ViewGroup层

public class CustomViewR extends RelativeLayout {
//省略构造方法
    private static final String TAG = "CustomViewR";

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
       switch (ev.getAction()){
           case MotionEvent.ACTION_DOWN:
               Log.e(TAG, "dispatchTouchEvent: ->"+"CustomR dispatch DOWN" );
              break;

       }
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, "onInterceptTouchEvent: ->"+"CustomR onIntercept DOWN" );
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, "onTouchEvent: --->"+"CustomR onTouchEvent DOWN");
                break;
        }
        return super.onTouchEvent(event);
    }
}

CustomViewButton表示View层

public class CustomViewButton extends AppCompatButton {
//省略构造方法
    private static final String TAG = "CustomViewButton";

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, "dispatchTouchEvent: ->"+"CustomButton dispatch DOWN" );
                break;
        }
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, "onTouchEvent: --->"+"CustomButton onTouchEvent DOWN" );
               break;
        }
        return super.onTouchEvent(event);
    }
}

<1>上述代码中默认返回父类的实现,那么看一下输出Log:

MainActivity: dispatchTouchEvent: ->Activity dispatch DOWN
CustomViewR: dispatchTouchEvent: ->CustomR dispatch DOWN
CustomViewR: onInterceptTouchEvent: ->CustomR onIntercept DOWN
CustomViewButton: dispatchTouchEvent: ->CustomButton dispatch DOWN
CustomViewButton: onTouchEvent: --->CustomButton onTouchEvent DOWN

可以看出,当以默认的父类的实现返回时,View的onTouchEvent会默认消耗事件,即返回true。

<2>还是以上述代码为例,当在View层的onTouchEvent中返回false,代码我就不再贴出来,看下输出日志:

MainActivity: dispatchTouchEvent: ->Activity dispatch DOWN
CustomViewR: dispatchTouchEvent: ->CustomR dispatch DOWN
CustomViewR: onInterceptTouchEvent: ->CustomR onIntercept DOWN
CustomViewButton: dispatchTouchEvent: ->CustomButton dispatch DOWN
CustomViewButton: onTouchEvent: --->CustomButton onTouchEvent DOWN
CustomViewR: onTouchEvent: --->CustomR onTouchEvent DOWN
MainActivity: onTouchEvent: ->Activity onTouchEvent DOWN

对比第一次的日志输出,可以明显的看出,后面多输出了两条数据,也就是说,在CustomViewButton中的onTouchEvent返回false,没有消费这次的点击事件,则回传给父容器中的onTouchEvent。

<3>再看下当我们在CustomViewR中的onInterceptTouchEvent中返回true,即拦截了当前事件。Log如下:

MainActivity: dispatchTouchEvent: ->Activity dispatch DOWN
CustomViewR: dispatchTouchEvent: ->CustomR dispatch DOWN
CustomViewR: onInterceptTouchEvent: ->CustomR onIntercept DOWN
CustomViewR: onTouchEvent: --->CustomR onTouchEvent DOWN
MainActivity: onTouchEvent: ->Activity onTouchEvent DOWN

上述代码可以看出,当onInterceptTouchEvent拦截了事件后,将不会再传递到下一层CustomViewButton中,而为什么又回传到了Activity 层的
onTouchEvent呢?是因为在ViewGrop层中的onTouchEvent 默认返回false,所以不消耗事件,把事件回传给了父容器。

总结:这一次View事件分发就写到这,结合图和代码的展示,相信这样看会看的比较明了。这里虽然只写了一个单一的Action_DOWN事件,但是View的分发机制中,一个事件产生后都会遵循刚才说的规则传递:Activity -> Window -> View
注: 一个事件的完成,表示从手指点击屏幕开始,直到离开屏幕的过程,即Action_DOWN开始,中间可能会有很多个Action_MOVE,以Action_UP结束。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值