• Android事件分发机制详解(1)----探究View的事件分发


    探究View的事件分发

    在Activity中,只有一个按钮,注册一个点击事件

    1. button.setOnClickListener(new OnClickListener() {  
    2.     @Override  
    3.     public void onClick(View v) {  
    4.         Log.d("TAG""onClick execute");  
    5.     }  
    6. });  
    如果在需要一个触摸事件
    1. button.setOnTouchListener(new OnTouchListener() {  
    2.     @Override  
    3.     public boolean onTouch(View v, MotionEvent event) {  
    4.         Log.d("TAG""onTouch execute, action " + event.getAction());  
    5.         return false;  
    6.     }  
    7. });  
       
    onTouch事件的动作比onClick要多,onTouch有Action_down,Action_move,Action_up.如果都注册了,到底谁先执行.
                
    可以看到,onTouch是先于onClick执行的.并且onTouch执行了两遍.因此事件的传递顺序是经过onTouch再到onClick的.
    但细心点会发现,onTouch是有返回值的.如果将onTouch的返回值改成return true.则会出现
                
    我们发现,onClick方法不再执行了!为什么会这样呢?你可以先理解成onTouch方法返回true就认为这个事件被onTouch消费掉了,因而不会再继续向下传递。
     
    如果到现在为止,以上的所有知识点你都是清楚的,那么说明你对Android事件传递的基本用法应该是掌握了。不过别满足于现状,让我们从源码的角度分析一下,出现上述现象的原理是什么。
     
    首先,无论摸到什么控件,一定会调用该控件的dispatchTouchEvent()方法,所以当我们点击按钮时,会调用Button的dispatchTouchEvent()方法,但是Button本身并没有这个方法,其父类TextView同样也没有,查看TextView的父类,会发现View中有.
           
     
     
    首先,View的dispatchTouchEvent方法源码:
     
    1. public boolean dispatchTouchEvent(MotionEvent event) {  
    2.     if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&  
    3.             mOnTouchListener.onTouch(this, event)) {  
    4.         return true;  
    5.     }  
    6.     return onTouchEvent(event);  
    7. }  
       
     
    很清楚,如果满足三个表达式,则会返回true,否则进入onTouchEvent()方法
     
    先看第一个条件,mOnTouchListener!=null  这里,只需要给控件注册了Touch事件,mOnTouchListener就一定不为空.
    第二个条件,则是判断当前控件是否是enable的,因为是Button,所以默认都是enable的.
    关键的是第三个条件,mOnTouchListener.onTouch(this,event), 如果在onTouch方法里面返回了true,则这个表达式成立,返回true,所以不会往下执行.因此onClick也不会被执行了.如果onTouch返回了false,则表达式会进入onTouchEvent()方法里.
    上面的分析还透漏出了一个重要的信息,那就是onClick的调用肯定是在onTouchEvent(event)方法中的.
     
    这样View的整个事件分发的流程就让我们搞清楚了,但还有一个重要知识点,就是touch事件的层级传递
    我们如果给一个控件注册了touch事件,每次点击都会触发一系列的Action_Down,Action_Move,Action_Up等事件.如果在Action_Down中返回了false,则后面的一系列action动作就不会触发了.
     
    简单的说,就是当dispatchTouchEvent在进行事件分发的时候,只有前一个action返回true,才会触发后一个action。
     
    说到这里,很多的朋友肯定要有巨大的疑问了。这不是在自相矛盾吗?前面的例子中,明明在onTouch事件里面返回了false,ACTION_DOWN和ACTION_UP不是都得到执行了吗?其实你只是被假象所迷惑了,让我们仔细分析一下,在前面的例子当中,我们到底返回的是什么。
     

    参考着我们前面分析的源码,首先在onTouch事件里返回了false,就一定会进入到onTouchEvent方法中,然后我们来看一下onTouchEvent方法的细节。由于我们点击了按钮,就会进入到第14行这个if判断的内部,然后你会发现,不管当前的action是什么,最终都一定会走到第89行,返回一个true。

    是不是有一种被欺骗的感觉?明明在onTouch事件里返回了false,系统还是在onTouchEvent方法中帮你返回了true。就因为这个原因,才使得前面的例子中ACTION_UP可以得到执行。

    那我们可以换一个控件,将按钮替换成ImageView,然后给它也注册一个touch事件,并返回false。如下所示:

    1. imageView.setOnTouchListener(new OnTouchListener() {  
    2.     @Override  
    3.     public boolean onTouch(View v, MotionEvent event) {  
    4.         Log.d("TAG""onTouch execute, action " + event.getAction());  
    5.         return false;  
    6.     }  
    7. });  
    运行一下程序,点击ImageView,你会发现结果如下:

     


    在ACTION_DOWN执行完后,后面的一系列action都不会得到执行了。这又是为什么呢?因为ImageView和按钮不同,它是默认不可点击的,因此在onTouchEvent的第14行判断时无法进入到if的内部,直接跳到第91行返回了false,也就导致后面其它的action都无法执行了。

    好了,关于View的事件分发,我想讲的东西全都在这里了。现在我们再来回顾一下开篇时提到的那三个问题,相信每个人都会有更深一层的理解

     
    ​onTouch与onTouchEvent的区别:
    从源码看,两个方法都是在View的dispatchTouchEvent中调用的,  onTouch是dispatchTouchEvent()方法中的一个条件,很大程度上决定是否消费掉这个事件.如果返回true消费掉事件,则不会进入onTouchEvent()方法中,因此更不会出现OnClick事件.如果返回false,则会进入onTouchEvent()方法.
     
    需要注意的是,如果想要onTouch能够执行,必须满足mTouchListener不为空,以及当前点击的控件是enable的,否者,该控件不会响应onTouch事件,如果需要onTouch响应,则需要重写onTouchEvent()方法.
     
     
    个人总结归纳:
    • (一般情况下)事件传递的顺序先经过onTouch,在传递onClick.
    • 如果onTouch返回true,则不会响应onClick方法.满足dispatchTouchEvent()方法的第三个表达式.返回true,不会进入onTouchEvent.
    • onClick事件的调用是在onTouchEvent方法中.
    • 控件的Touch事件,如果Action_Down结果返回false,则不会触发后面一系列action动作,但用Button却能触发,因为在onTouchEvent方法中最终会返回true.ImageView却不能,因为ImageView默认不可点击.
    • 控件的Touch最开始会在dispatchTouchEvent()响应.
     
     
     
     
     
     
     





    qq3061280@163.com
  • 相关阅读:
    程序员面试金典-整数对查找
    hihocoder-1552-缺失的拼图
    论文: YOLO9000-Better,Faster,Stronger
    hihocoder-1524-逆序对
    hihocoder-1546-集合计数
    hihocoder-1543-SCI表示法
    Oracle中的定时任务JOB
    JS中时间戳处理
    Boostrap小技巧
    Struts标签 logic:iterate简单使用
  • 原文地址:https://www.cnblogs.com/aibuli/p/d107a430916ffbe565828a09a5c0a217.html
Copyright © 2020-2023  润新知