zoukankan      html  css  js  c++  java
  • 通俗理解Android事件分发与消费机制

    深入:Android Touch事件传递机制全面解析(从WMS到View树)

    通俗理解Android事件分发与消费机制

      说起Android滑动冲突,是个很常见的场景,比如SliddingMenu与ListView的嵌套,要解决滑动冲突,不得不提及到View的事件分发机制。


      Touch事件传递规则分析
    首先,我们要知道Touch事件是包装在MotionEvent对象中的,在手指与屏幕接触过程中产生一系列事件,典型的事件有以下三种:
    ACTION_DOWN:手指刚接触屏幕的瞬间

    ACTION_MOVE:手指在屏幕上滑动

    ACTION_UP:手指刚离开屏幕的瞬间

     

    那么,Android中Touch事件是一个怎样的传递过程呢?

     

     1 , 事件分发:public boolean dispatchTouchEvent(MotionEvent ev)

    Touch事件发生时Activity的dispatchTouchEvent(MotionEvent ev)方法会将事件传递给最外层View的dispatchTouchEvent(MotionEvent ev)方法,该方法对事件进行分发。分发逻辑如下:
        如果return true,事件会由当前View的dispatchTouchEvent方法进行消费,同时事件会停止向下传递;

        如果return false,事件分发分为两种情况:
          如果当前 View 获取的事件直接来自 Activity,则会将事件返回给Activity的onTouchEvent进行消费;
          如果当前 View 获取的事件来自外层父控件,则会将事件返回给父View的onTouchEvent进行消费。

        如果return super.dispatchTouchEvent(ev),事件分发分为两种情况:

         如果当前View是ViewGroup,则事件会分发给onInterceptTouchEvent方法进行处理;

         如果当前View是普通View,则事件直接交给onTouchEvent方法进行处理

       2, 事件拦截:public boolean onInterceptTouchEvent(MotionEvent ev)

    此方法只有ViewGroup才有, Activity与普通View没有。上面已经提到,如果当前ViewGroup的dispatchTouchEvent(事件分发)返回super.dispatchTouchEvent(ev), 那么事件会传递到传递到onInterceptTouchEvent方法, 该方法对事件进行拦截。拦截逻辑如下:
        如果return true,则表示拦截该事件,并将事件交给当前View的onTouchEvent方法;

        如果return false,则表示不拦截该事件,并将该事件交由子View的dispatchTouchEvent方法进行事件分发,重复上述过程;

        如果return super.onInterceptTouchEvent(ev), 事件拦截分两种情况:       

           如果该View(ViewGroup)存在子View且点击到了该子View, 则不拦截, 继续分发给子View 处理, 此时相当于return false。

         如果该View(ViewGroup)没有子View或者有子View但是没有点击中子View(此时ViewGroup相当于普通View), 则交由该View的onTouchEvent响应,此时相当于return true。 

    一般的LinearLayout、 RelativeLayout、FrameLayout等ViewGroup默认不拦截, 而ScrollView、ListView等ViewGroup则可能拦截,得看具体情况。

       3, 事件响应:public boolean onTouchEvent(MotionEvent ev)

    上面已经提到,在dispatchTouchEvent(事件分发)返回super.dispatchTouchEvent(ev)并且onInterceptTouchEvent进行拦截(事件拦截返回true)的情况下,那么事件会传递到onTouchEvent方法,该方法对事件进行响应。响应逻辑如下:
        如果return true,则表示响应并消费该事件;
        如果return fasle,则表示不响应事件,那么该事件将会不断向上层View的onTouchEvent方法传递,直到某个View的onTouchEvent方法返回true,如果到了最顶层View还是返回false,那么认为该事件不消耗,则在同一个事件系列中,当前View无法再次接收到事件,该事件会交由Activity的onTouchEvent进行处理;
        如果return super.dispatchTouchEvent(ev),事件处理分为两种情况:

        如果该View是clickable或者longclickable的,则会返回true, 表示消费了该事件, 与返回true一样;

        如果该View不是clickable或者longclickable的,则会返回false, 表示不消费该事件,将会向上传递,与返回false一样.

     

    上述三个方法到底有什么区别与联系呢?我们通过一段伪代码来表示:

     事件分发伪代码

    [java] view plain copy
     
    1. public boolean dispatchTouchEvent(MotionEvent ev){  
    2.     boolean consume = false;  
    3.     if(onInterceptTouchEvent(ev)){ // 如果onInterceptTouchEvent返回true  
    4.         consume = onTouchEvent(ev);  // 则交由该View的onTouchEvent方法  
    5.     } else {  
    6.         consume = child. dispatchTouchEvent(ev); // 否则交由子View的dispatchTouchEvent事件进行分发  
    7.     }  
    8.     return consume; // 如果成功消费该事件,则返回true,然后停止传递,否则返回false  
    9. }  

     -------------------------------------------------------------------分割线------------------------------------------------------------

    各组件对应方法的有无情况:

     注: Activity的dispatchTouchEvent最终是调用了Window对应DecorView的dispatchTouchEvent, 相当于ViewGroup; onTouchEvent是Activity自带的方法并不是DecorView的onTouchEvent; 同时,没有onInterceptTouchEvent方法是因为Window并没有回调该方法。

    返回值作用:true和false标志事件是否被消费。

    如果消费了就不再传递给其他控件了,如果没有消费则还会传递给父控件或者子控件,触发相应控件的事件处理函数。

    控件默认返回值

    1,对于ViewGroup的onInterceptTouchEvent方法:

      如果存在子View且点击到了子View, 则不拦截, 继续分发给子View 处理, 此时返回super.onInterceptTouchEvent(ev) 就相当于return false。

      如果没有子View或者有子View但是没有点击中子View(此时ViewGroup相当于普通View), 则交由当前View的onTouchEvent响应,此时返回super.onInterceptTouchEvent(ev) 相当于return true。 

    2,对于View的onTouchEvent方法: 如果是clickable或者longClickable的,则返回true消费该事件; 否则返回false不消费该事件,从而往上传递.

     -------------------------------------------------------------------分割线------------------------------------------------------------

     注:同一个事件序列是指从手指接触屏幕的那一刻开始,到手指离开屏幕那一刻结束,在这过程中所产生的一系列事件,这个事件序列以down事件开始,以up事件结束,中间含有数量不定的move事件.

    事件分发与消费的规则总结:   

      (1)事件的分发是以隧道方式由上到下的, 即事件总是先传递给父元素, 然后再由父元素分发给子元素。对于onTouchEvent事件,如果返回false,则会以冒泡方式向上传递。 顶级View接收到事件之后,就会按相应规则去分发事件。如果一个View的onTouchEvent方法返回false,那么将会交给父容器的onTouchEvent方法进行处理,以冒泡方式逐级往上,如果所有的View都不处理该事件,则交由Activity的onTouchEvent进行处理。就跟工作中遇到了难题,逐级找领导解决一个道理,领导解决不了,再找上一级领导。

        (2)正常情况下,一个事件序列只能被一个View拦截且消耗。某个View一旦进行事件拦截,那么这一个事件序列都只能交由他处理,并且onInterceptTouchEvent也不会被再次调用。因此,正常情况下一个事件是不能交给两个View来处理的,当然,特殊做法就是在View的onTouchEvent处理完之后再返回false,强行交给其他View处理。

        (3)如果某一个View开始处理事件,如果他不消耗ACTION_DOWN事件(也就是onTouchEvent返回false),则同一事件序列比如接下来进行ACTION_MOVE、ACTION_UP,则不会再交给该View处理,并且事件将重新提交给它的父元素处理。就像工作中做一件事情,你要么做完,要么你就不要做这件事了。

        (4)ViewGroup的onInterceptTouchEvent方法默认返回false,即不拦截任何事件,而交给子View进行分发处理(前提是有子View)。

        (5)普通View(比如TextView、ImageView,非ViewGroup)没有onInterceptTouchEvent方法, 一旦有事件传递给它,它的onTouchEvent方法就会被调用。正常情况下,它们都会消耗事件(返回true),除非它们是不可点击的(clickable和longClickable都为false),那么就会交由父容器的onTouchEvent处理。View的longClickable默认都是false的,而对于clickable则要分情况,比如Button的clickable默认你是true,而TextView默认是false.

        (6)如果View不消耗除down以外的其他事件, 此时父View的onTouchEvent并不会被调用, 并且当前View可以持续收到后续事件,最终这些事件会传递给Activity处理.

      (7)View的enable属性不影响onTouchEvent的默认返回值,只要它clickable或者longClickable为true,则onTouchEvent就会返回true。

      (8)  如果当前View是可点击的,并且它收到了down和up事件(以down开始,以up结束),则它的click事件就会触发;对于onLongClick,则只要当前View是longClickable的并接收到down事件且超过了系统默认的long时间,则就会触发,只与down事件有关而与up事件无关.

        (9)点击事件分发过程如下 dispatchTouchEvent—->OnTouchListener的onTouch方法—->onTouchEvent-->OnClickListener的onClick方法。也就是说,我们平时调用的setOnClickListener,优先级是最低的,所以,OnTouchListener的onTouch方法如果返回true,则不响应onClick方法...

      (10) 子View可以通过requestDisallowInterceptTouchEvent方法请求父控件不要拦截事件,从而干预事件的分发过程,但是down事件除外,无法干预到

     (11)如果一个View监听了onTouch,则在onTouch里面应该返回false,否则onTouchEvent事件及点击、长按事件就无法监听到

       (12)如果ViewGroup中的子View将传递的事件消费掉,ViewGroup的onTouch将无法接收到任何事件, 但onTouchEvent还是能接收到的;  如果是View的onTouchEvent消费,则该View的onTouch仍然能接收到事件,因为此时onTouch的调用在onTouchEvent之前。   总之,对于View, 无论onTouchEvent消费与否,都会触发View的onTouch事件, 因为onTouch的调用在onTouchEvent之前。

     

     -------------------------------------------------------------------分割线------------------------------------------------------------

    【参考资料】

    Trinea 

    Android Touch事件传递机制

    郭林

    Android事件分发机制完全解析,带你从源码的角度彻底理解(上) 

    Android事件分发机制完全解析,带你从源码的角度彻底理解(下) 

    鸿洋

    Android  View事件分发机制 源码解析 (上)

    Android ViewGroup事件分发机制 源码解析(下) 

    工匠若水

    Android触摸屏事件派发机制详解与源码分析一(View篇)

    Android触摸屏事件派发机制详解与源码分析二(ViewGroup篇)

    Android触摸屏事件派发机制详解与源码分析三(Activity篇) 

    AigeStudio 

    Android事件分发完全解析之为什么是她 

    Android事件分发完全解析之事件从何而来

     

    click相关:  

    Android中onTouchEvent, onClick及onLongClick的调用机制(一) 

    Android中onTouchEvent, onClick及onLongClick的调用机制(二)

  • 相关阅读:
    hdu 1042 N!
    hdu 1002 A + B Problem II
    c++大数模板
    hdu 1004 Let the Balloon Rise
    hdu 4027 Can you answer these queries?
    poj 2823 Sliding Window
    hdu 3074 Multiply game
    hdu 1394 Minimum Inversion Number
    hdu 5199 Gunner
    九度oj 1521 二叉树的镜像
  • 原文地址:https://www.cnblogs.com/wytiger/p/5235393.html
Copyright © 2011-2022 走看看