zoukankan      html  css  js  c++  java
  • Android 事件响应原理

    一、简介

      触摸事件就是捕获触摸屏幕后产生的事件。Android为触摸事件封装了一个类——MotionEvent,如果重写onTouchEvent(MotionEvent event)方法,就会发现该方法的参数就是一个MotionEvent类实例。

      事件触发分为三个阶段,分发、拦截、消费。

    二、事件拦截机制分析

      Android的View结构就是一个树形结构,View可以放在一个ViewGroup里面,这个ViewGroup又放在另一个ViewGroup里面,甚至还有可能继续嵌套,一层层地叠起来。触摸事件就一个,到底该分给谁?同一个事件,子View和父ViewGroup都有可能想要进行处理,因此就产生了“事件拦截机制”。

      假设,你所在公司,有一个总经理,级别最高;他下面有一个部长,级别次之;最低层,就是干活的你,没有级别。现在董事会交给总经理一个任务,总经理将这项任务布置给了部长,部长又把任务安排给了你。而当你完成了这项任务,你就把任务交给部长,部长觉得任务完成的不错,于是就签了他的名字,将任务交给了总经理,总经理看了也觉得不错,就也签了名字交给董事会。这个,一个任务就顺利完成了。

      一个总经理——MyViewGroupA,最外层的ViewGroup;

      一个部长——MyViewGroupB,中间层ViewGroup;

      一个干活的你——MyView,在最低层;

      对于ViewGroup,需要重写三个方法:

     1 @Override
     2 public boolean dispatchTouchEvent(MotionEvent event)
     3 {
     4     Log.d(TAG, "ViewGroup dispatchTouchEvent" + event.getAction());
     5     return super.dispatchTouchEvent(event);
     6 }
     7 
     8 @Override
     9 public boolean onInterceptTouchEvent(MotionEvent event)
    10 {
    11     Log.d(TAG, "ViewGroup onInterceptTouchEvent" + event.getAction());
    12     return super.onInterceptTouchEvent(event);
    13 }
    14 
    15 @Override
    16 public boolean onTouchEvent(MotionEvent event)
    17 {
    18     Log.d(TAG, "ViewGroup onTouchEvent" + event.getAction());
    19     return super.onTouchEvent(event);
    20 }

      对于View,需要重写如下两个方法:

     1 @Override
     2 public boolean dispatchTouchEvent(MotionEvent event)
     3 {
     4     Log.d(TAG, "View dispatchTouchEvent" + event.getAction());
     5     return super.dispatchTouchEvent(event);
     6 }
     7 
     8 @Override
     9 public boolean onTouchEvent(MotionEvent event)
    10 {
    11     Log.d(TAG, "View onTouchEvent" + event.getAction());
    12     return super.onTouchEvent(event);
    13 }

      当点击View后的Log如下所示:

    1 D/MyViewGroupA : ViewGroupA dispatchTouchEvent
    2 D/MyViewGroupA : ViewGroupA onInterceptTouchEvent
    3 D/MyViewGroupB : ViewGroupB dispatchTouchEvent
    4 D/MyViewGroupB : ViewGroupB onInterceptTouchEvent
    5 D/MyView : View dispatchTouchEvent
    6 D/MyView : View onTouchEvent
    7 D/MyViewGroupB : MyViewGroupB onTouchEvent
    8 D/MyViewGroupA : MyViewGroupA onTouchEvent
      从其中可以看到,事件传递顺序是:

        总经理(MyViewGroupA)— 部长(MyViewGroupB)— 你(MyView),这个过程就是事件的分发阶段;

      PS:事件传递,即分发,就是执行dispatchTouchEvent()方法,再执行onInterceptTouchEvent()方法。

      事件的处理顺序:

        在你去干活的过程就是事件的目标阶段,即View的onTouchEvent()方法执行;

        你(MyView)— 部长(MyViewGroupB)— 总经理(MyViewGroupA),这个过程就是事件的向上传递消费阶段;

      PS:事件处理就是执行onTouchEvent()方法。

       假设1:总经理(MyViewGroupA)发现这个任务太简单了,觉得自己就可以完成,完全没必要再找下属。因此,事件就被总经理(MyViewGroupA)onInterceptTouchEvent()方法把事件拦截了,即让onInterceptTouchEvent()方法返回值为True,下面再看看Log:

    1 D/MyViewGroupA : ViewGroupA dispatchTouchEvent
    2 D/MyViewGroupA : ViewGroupA onInterceptTouchEvent
    3 D/MyViewGroupA : MyViewGroupA onTouchEvent

      假设2:部长(MyViewGroupB)发现这个任务太简单了,觉得自己就可以完成,完全没必要再找下属。因此,事件就被部长(MyViewGroupB)onInterceptTouchEvent()方法把事件拦截了,即让onInterceptTouchEvent()方法返回值为True,下面再看看Log: 

    1 D/MyViewGroupA : ViewGroupA dispatchTouchEvent
    2 D/MyViewGroupA : ViewGroupA onInterceptTouchEvent
    3 D/MyViewGroupB : ViewGroupB dispatchTouchEvent
    4 D/MyViewGroupB : ViewGroupB onInterceptTouchEvent
    5 D/MyViewGroupB : MyViewGroupB onTouchEvent
    6 D/MyViewGroupA : MyViewGroupA onTouchEvent

      这两种情况下,可以看到总经理或者部长,MyViewGroupA或者MyViewGroupB将事件拦截了。

      事件的返回值含义:

      当事件在传递的过程中,返回值:True,拦截,不继续;False,不拦截,继续流程,传递给下一级。

      当事件在处理的过程中,返回值:True,处理了,不用审核了;False,交给上一级处理。

    三、总结

    1. dispatchTouchEvent(...):

      作用:决定事件是否由onInterceptTouchEvent拦截处理;

      当返回super.dispatchTouchEvent(...)时,由onInterceptTouchEvent来决定事件的流向,onInterceptTouchEvent返回值为false时,继续向子View分发事件,本View只处理ACTION_DOWN事件。

      当onInterceptTouchEvent返回值为true时,不继续向子View分发事件,本View处理所有事件。

    2. onInterceptTouchEvent(...):

      作用:拦截事件,决定是否将事件传递给子View;

      当返回值为false时,事件继续传递给子View;

      当返回值为true时,事件交给onTouchEvent(...)处理,不再向子View传递。

    3. onTouchEvent(MotionEvent event):

      作用:事件最终到这个方法,由此方法处理事件;

      当返回值为false时,事件继续向上传递给其父View的onTouchEvent()方法,直到根View的,一直传递到根View时,返回值都是false,也就是说在事件传递过程中没有View的onTouchEvent()方法返回true,此次手势就会结束,此次事件就会被取消。

      当返回值为true时,此View处理所有事件,并且处理的事件不会再向上返回。也就是说在值为true时,此手势的所有事件都传递给此View的onTouchEvent()处理,包括ACTION_DOWN、ACTION_UP、ACTION_MOVE等事件。

    四、举例说明

      自定义三个View,分别为ViewEventA、ViewEventB、ViewEventC:

      1 // ViewEventA
      2 public class ViewEventA extends ViewGroup {
      3     private static final String TAG = "ViewEventA";
      4 
      5     public ViewEventA(Context context) {
      6         super(context);
      7     }
      8 
      9     @Override
     10     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
     11         int count = getChildCount();
     12         for (int idx = 0; idx < count; idx++) {
     13             View child = getChildAt(idx);
     14             int widthMeSpec = MeasureSpec.makeMeasureSpec(300, MeasureSpec.EXACTLY);
     15             int heightmeSpec = MeasureSpec.makeMeasureSpec(300, MeasureSpec.EXACTLY);
     16             child.measure(widthMeSpec, heightmeSpec);
     17         }
     18         int widthMeSpec = MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY);
     19         int heightmeSpec = MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY);
     20         setMeasuredDimension(widthMeSpec, heightmeSpec);
     21     }
     22 
     23     @Override
     24     protected void onLayout(boolean changed, int l, int t, int r, int b) {
     25         if (changed) {
     26             int count = getChildCount();
     27             for (int idx = 0; idx < count; idx++) {
     28                 View child = getChildAt(idx);
     29                 child.layout(0, 0, 300, 300);
     30             }
     31         }
     32     }
     33 
     34     @Override
     35     public boolean dispatchTouchEvent(MotionEvent event) {
     36         Log.d(TAG, "dispatchTouchEvent");
     37         return super.dispatchTouchEvent(event);
     38     }
     39 
     40     @Override
     41     public boolean onInterceptTouchEvent(MotionEvent event) {
     42         Log.d(TAG, "onInterceptTouchEvent");
     43         return super.onInterceptTouchEvent(event);
     44     }
     45 
     46     @Override
     47     public boolean onTouchEvent(MotionEvent event) {
     48         String action = "";
     49         switch (event.getAction()) {
     50             case MotionEvent.ACTION_DOWN:
     51                 action = "ACTION_DOWN";
     52                 break;
     53             case MotionEvent.ACTION_MOVE:
     54                 action = "ACTION_MOVE";
     55                 break;
     56             case MotionEvent.ACTION_UP:
     57                 action = "ACTION_UP";
     58                 break;
     59             case MotionEvent.ACTION_CANCEL:
     60                 action = "ACTION_CANCEL";
     61                 break;
     62         }
     63         Log.d(TAG, "onTouchEvent: " + action);
     64         return super.onTouchEvent(event);
     65     }
     66 }
     67 
     68 // ViewEventB
     69 public class ViewEventB extends ViewGroup {
     70     private static final String TAG = "ViewEventB";
     71 
     72     public ViewEventB(Context context) {
     73         super(context);
     74     }
     75 
     76     @Override
     77     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
     78         int count = getChildCount();
     79         for (int idx = 0; idx < count; idx++) {
     80             View child = getChildAt(idx);
     81             int width = MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY);
     82             int height = MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY);
     83             child.measure(width, height);
     84         }
     85 
     86         setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
     87     }
     88 
     89     @Override
     90     protected void onLayout(boolean changed, int l, int t, int r, int b) {
     91         if (changed) {
     92             int count = getChildCount();
     93             for (int idx = 0; idx < count; idx++) {
     94                 View child = getChildAt(idx);
     95                 child.layout(0, 0, 100, 100);
     96             }
     97         }
     98     }
     99 
    100     @Override
    101     public boolean dispatchTouchEvent(MotionEvent event) {
    102         Log.d(TAG, "dispatchTouchEvent");
    103         return super.dispatchTouchEvent(event);
    104     }
    105 
    106     @Override
    107     public boolean onInterceptTouchEvent(MotionEvent event) {
    108         Log.d(TAG, "onInterceptTouchEvent");
    109         return super.onInterceptTouchEvent(event);
    110     }
    111 
    112     @Override
    113     public boolean onTouchEvent(MotionEvent event) {
    114         String action = "";
    115         switch (event.getAction()) {
    116             case MotionEvent.ACTION_DOWN:
    117                 action = "ACTION_DOWN";
    118                 break;
    119             case MotionEvent.ACTION_MOVE:
    120                 action = "ACTION_MOVE";
    121                 break;
    122             case MotionEvent.ACTION_UP:
    123                 action = "ACTION_UP";
    124                 break;
    125             case MotionEvent.ACTION_CANCEL:
    126                 action = "ACTION_CANCEL";
    127                 break;
    128         }
    129         Log.d(TAG, "onTouchEvent: " + action);
    130         return super.onTouchEvent(event);
    131     }
    132 }
    133 
    134 // ViewEventC
    135 public class ViewEventC extends ViewGroup {
    136     private static final String TAG = "ViewEventC";
    137 
    138     public ViewEventC(Context context) {
    139         super(context);
    140     }
    141 
    142     @Override
    143     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    144         setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
    145     }
    146 
    147     @Override
    148     protected void onLayout(boolean changed, int l, int t, int r, int b) {
    149 
    150     }
    151 
    152     @Override
    153     public boolean dispatchTouchEvent(MotionEvent event) {
    154         Log.d(TAG, "dispatchTouchEvent");
    155         return super.dispatchTouchEvent(event);
    156     }
    157 
    158     @Override
    159     public boolean onInterceptTouchEvent(MotionEvent event) {
    160         Log.d(TAG, "onInterceptTouchEvent");
    161         return super.onInterceptTouchEvent(event);
    162     }
    163 
    164     @Override
    165     public boolean onTouchEvent(MotionEvent event) {
    166         String action = "";
    167         switch (event.getAction()) {
    168             case MotionEvent.ACTION_DOWN:
    169                 action = "ACTION_DOWN";
    170                 break;
    171             case MotionEvent.ACTION_MOVE:
    172                 action = "ACTION_MOVE";
    173                 break;
    174             case MotionEvent.ACTION_UP:
    175                 action = "ACTION_UP";
    176                 break;
    177             case MotionEvent.ACTION_CANCEL:
    178                 action = "ACTION_CANCEL";
    179                 break;
    180         }
    181         Log.d(TAG, "onTouchEvent: " + action);
    182         return super.onTouchEvent(event);
    183     }
    184 }    

      场景1:

      当本三个嵌套View中onTouchEvent()方法返回值均为false时,输出:

    1 D/ViewEventA: dispatchTouchEvent
    2 D/ViewEventA: onInterceptTouchEvent
    3 D/ViewEventB: dispatchTouchEvent
    4 D/ViewEventB: onInterceptTouchEvent
    5 D/ViewEventC: dispatchTouchEvent
    6 D/ViewEventC: onInterceptTouchEvent
    7 D/ViewEventC: onTouchEvent: ACTION_DOWN
    8 D/ViewEventB: onTouchEvent: ACTION_DOWN
    9 D/ViewEventA: onTouchEvent: ACTION_DOWN

      从结果看出onTouchEvent()方法返回值均为false直到根View,事件消失了,手势被取消了。

      场景2:

      当本三个嵌套View中第二个View的onTouchEvent()方法返回值为true时,中间View,输出:

     1 D/ViewEventA: dispatchTouchEvent
     2 D/ViewEventA: onInterceptTouchEvent
     3 D/ViewEventB: dispatchTouchEvent
     4 D/ViewEventB: onInterceptTouchEvent
     5 D/ViewEventC: dispatchTouchEvent
     6 D/ViewEventC: onInterceptTouchEvent
     7 D/ViewEventC: onTouchEvent: ACTION_DOWN
     8 D/ViewEventB: onTouchEvent: ACTION_DOWN
     9 D/ViewEventA: dispatchTouchEvent
    10 D/ViewEventA: onInterceptTouchEvent
    11 D/ViewEventB: dispatchTouchEvent
    12 D/ViewEventB: onTouchEvent: ACTION_MOVE
    13 D/ViewEventA: dispatchTouchEvent
    14 D/ViewEventA: onInterceptTouchEvent
    15 D/ViewEventB: dispatchTouchEvent
    16 D/ViewEventB: onTouchEvent: ACTION_MOVE
    17 D/ViewEventA: dispatchTouchEvent
    18 D/ViewEventA: onInterceptTouchEvent
    19 D/ViewEventB: dispatchTouchEvent
    20 D/ViewEventB: onTouchEvent: ACTION_UP

      从结果看出事件在向上冒泡返回时,发现第二个View的onTouchEvent()方法返回值为true时,此View消化了此次事件,并且此手势的其它事件均由第二个View处理。在第二个事件ACTION_UP从根View向下传递时,直到第二个View消化了此次事件,并且不再向下传递事件,同时,在事件被消化后也未再传递此事件。

      场景三:

      在第二个View中使用onInterceptTouchEvent()方法拦截事件,即第二个View的onInterceptTouchEvent()方法返回值为true,结果输出:

    1 D/ViewEventA: dispatchTouchEvent
    2 D/ViewEventA: onInterceptTouchEvent
    3 D/ViewEventB: dispatchTouchEvent
    4 D/ViewEventB: onInterceptTouchEvent
    5 D/ViewEventB: onTouchEvent: ACTION_DOWN
    6 D/ViewEventA: onTouchEvent: ACTION_DOWN

      从结果看出第二个View拦截了手势的ACTION_DOWN事件,事件未再向下(第三个)子View传递,再是在第二个View时,将事件交给了onTouchEvent()方法处理,但是由于第二个View的onTouchEvent()方法返回值为false,事件继续向上(父View)传递。发现没有View处理此次事件,手势被系统取消。

      场景4:

      当第二个View的onInterceptTouchEvent()方法和onTouchEvent()方法返回值均为true时,拦截事件并消化事件,结果输出:

     1 D/ViewEventA: onInterceptTouchEvent
     2 D/ViewEventB: dispatchTouchEvent
     3 D/ViewEventB: onInterceptTouchEvent
     4 D/ViewEventB: onTouchEvent: ACTION_DOWN
     5 D/ViewEventA: dispatchTouchEvent
     6 D/ViewEventA: onInterceptTouchEvent
     7 D/ViewEventB: dispatchTouchEvent
     8 D/ViewEventB: onTouchEvent: ACTION_MOVE
     9 D/ViewEventA: dispatchTouchEvent
    10 D/ViewEventA: onInterceptTouchEvent
    11 D/ViewEventB: dispatchTouchEvent
    12 D/ViewEventB: onTouchEvent: ACTION_UP

      结果与场景二一致。

      

      

     

  • 相关阅读:
    UIScrollerView遇到UINavigationController
    iOS 自动化打包
    最最基本的SQL常用命令
    导入样式表与外部样式表的区别
    jdk、jre、JVM的简单区别与联系
    JDBC驱动的四种类型
    将映射中的值进行排序并输出键
    Java优先级队列
    Java线程池
    Callable--创建有返回值的线程
  • 原文地址:https://www.cnblogs.com/naray/p/5623923.html
Copyright © 2011-2022 走看看