zoukankan      html  css  js  c++  java
  • 安卓中的事件分发机制之View控件

    前言:Android 中与 Touch 事件相关的方法包括:dispatchTouchEvent(MotionEvent ev)、onInterceptTouchEvent(MotionEvent ev)、onTouchEvent(MotionEvent ev);能够响应这些方法的控件包括:ViewGroup、View、Activity。继承ViewGroup的大多是容器控件,如LinearLayout等,而继承View的大部分是显示控件比如TextView,ImageView等(当然,ViewGroup本身是继承View的),显示控件没有onInterceptTouchEvent。

    我们知道,如果要给一个按钮注册一个点击事件,则代码如下:

    button.setOnClickListener(new OnClickListener() {
    	@Override
    	public void onClick(View v) {
    		Log.d("TAG", "onClick execute");
    	}
    });
    我们还知道,如果要给一个按钮注册一个触摸事件,则代码如下:

    button.setOnTouchListener(new OnTouchListener() {
    	@Override
    	public boolean onTouch(View v, MotionEvent event) {
    		Log.d("TAG", "onTouch execute, action " + event.getAction());
    		return false;
    	}
    });

    但是当这两个事件同时注册,则那个事件会先被执行呢?我们通过运行测试结果如下:


    可以看到,onTouch事件先执行的,然后才执行的onClick事件,而且onTouch事件执行了两次,这是因为触摸事件的响应动作比点击多多了,当你触摸一个控件时至少会经过A,CTION_DOWN和ACTION_UP,所以通过运行结果我们可以知道:当触摸与点击同时注册到一个控件上时,会先执行onTouch然后执行onClick事件。

    但是聪明的你可能就会想到,如果我在滑动一个控件的时候,最终会执行onClick方法,岂不是会导致onClick方法中的代码被执行,如:QQ中的LIstView可以向左滑动显示出删除该条目按钮,点击该item可以进入到chatActivity中查看会话,这样岂不当滑动时最终会执行onClick进入到chatActivity中,这明显不符合使用逻辑,能想到这说明你是一个爱思考的人,安卓设计人员早就帮我们考虑到了当onTouch与onClick同时注册时如何控制它们的运行流程,正如你所看到的,onTouch方法是有返回值的,在上述代码中我们采用的是IDE自动生成的代码,默认返回的是false。我们把它的返回值改为true,然后再运行上述程序,结果如下:


    可以看到,这次只执行了onTouch方法,而没执行onClick,讲到这就必须提一个概念:安卓中的事件分发机制。可以理解为当在一个控件注册的onTouch方法中返回true的时候,将不会执行该控件注册的onClick事件。为何是这样的呢?这就需要从源码中找答案了。

    首先我们知道,当触摸一个控件时,触摸动作会被Activity捕获到,调用Activity的dispathTouchEVent,然后会执行该控件的dispatchTouchEvent,接着执行控件的onTouch方法(如果注册了的话),带onTouch执行完,如果返回值为false,如上述情形,

    会把touch传递给onTouchEvent,注意最后在onTouchEvent中还调用了onClick方法。具体流程图示如下:


    基于上述的分析,我们先看看View中的dispatchTouchEvent源码,如下:

    public boolean dispatchTouchEvent(MotionEvent event) {
        if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
                mOnTouchListener.onTouch(this, event)) {
            return true;
        }
        return onTouchEvent(event);
    }
    可以看到代码非常简洁,就是一个if语句判断一个逻辑是否满足,如果满足则返回true,否则返回onTouchEvent(event),那么该逻辑的作用是什么呢?其实就是是否执行下面的onTouchEvent方法,在这个逻辑中存在三个条件,下面我们一个一个分析:

    第一个条件:mOnTouchListener != null ,那我们怎么知道mOnTouchListener 是否为null呢,这就要看mOnTouchListener 是在哪里被赋值,其实是在View类的setOnTouchListener中,代码如下:

    public void setOnTouchListener(OnTouchListener l) {
        mOnTouchListener = l;
    }
    所以自然就知道,如果该控件注册了onTouch事件,则 mOnTouchListener 的值就不会为null。

    再来看第二个条件:mViewFlags & ENABLED_MASK) == ENABLED,顾名思义就是判断该控件是否可以enable,在上述情形中我们用到的是button控件,默认是enable的,所以该值恒为true。

    最后看第三个条件:mOnTouchListener.onTouch(this, event),很明显就是去调用onTouch方法,所以如果onTouch方法返回true,则该参数为true,否则为false,整个if的逻辑判断语句为假,会执行下面的return onTouchEvent(event)方法。

    这样也就从源码的角度解释了上面我们测试的运行结果,现总结如下:

    1当我们给一个控件同时注册onTouch与onClick的话,则onTouch会先执行,且执行流程按照如下顺序:控件所在布局的dispatchTouchEvent------>控件的dispatchTouchEvent------->控件的onTouch-------->控件的onTouchEvent-------->控件的onClcik。

    2如果在触摸一个控件的时候,要屏蔽掉onClick则需要在注册的onTouchListener的onTouch中返回true,这样dispatchTouchEvent中的if语句第三个条件为true,这样就会直接返回true,不会执行onTouchEvent(event).

    3onTouch能够得到执行需保证两个前提条件,第一mOnTouchListener的值不能为空,第二当前点击的控件必须是enable的。如果控件是非enable的,那么给它注册onTouch事件将永远得不到执行。对于这一类控件,如果我们想要监听它的touch事件,则必须通过在该控件中重写onTouchEvent方法来实现。

    4.通过上述分析,我们可以推断出onClick是在onTouchEvent中执行的。




  • 相关阅读:
    服务注册与发现
    回溯算法
    SpringCloud之远程调用OpenFeign和Ribbon
    SpringCloud之Ribbon负载均衡策略
    Java(Springboot)获取客户端IP地址工具类
    Linux磁盘分区、挂载、查看文件大小
    Dubbo高级进阶Spi应用
    Dubbo监控平台DubboAdmin安装监控
    Dubbo高级进阶Spi应用以及与JDK的Spi区别
    python--装饰器的常见使用
  • 原文地址:https://www.cnblogs.com/hainange/p/6334059.html
Copyright © 2011-2022 走看看