Android 分发事件机制

来源:互联网 发布:淘宝商城女休闲鞋 编辑:程序博客网 时间:2024/05/16 09:32

前言

当Android系统捕获到用户的各种输入事件后,如何准确地传递给真正需要这个事件的控件呢?Android给我们提供了一整套完善的事件传递、处理机制,来帮助开发者完成准确的事件分配和处理。

基础知识

  1. MotionEvent
    • 所有触摸事件都被封装成MotionEvent对象,包括Touch的位置、时间、历史记录以及第几个手指(多指触摸)等。
    • 一次完整的MotionEvent事件,是从用户触摸屏幕到离开屏幕。整个过程的动作可如示:ACTION_DOWNACTION_DOWN(1次)-> ACTION_MOVE(N次)- > ACTION_UP(1次)
    • 多点触摸,每一个触摸点Pointer会有一个id和index。对于多指操作,通过pointerindex来获取指定Pointer的触屏位置。比如,对于单点操作时获取x坐标通过getX(),而多点操作获取x坐标通过getX(pointerindex)
  2. View
    • View是所有视图对象的父类,实现了动画相关的接口Drawable.Callback, 按键相关的接口KeyEvent.Callback, 交互相关的接口AccessibilityEventSource。比如Button继承自View。
    • TouchEvent事件处理相关的方法:
      dispatchTouchEvent(MotionEvent event)
      onTouchEvent(MotionEvent event)
  3. ViewGroup
    • ViewGroup,是一个abstract类,一组View的集合,可以包含View和ViewGroup,是所有布局的父类或间接父类。继承了View,实现了ViewParent(用于与父视图交互的接口), ViewManager(用于添加、删除、更新子视图到Activity的接口)。比如常用的LinearLayout,RelativeLayout都是继承自ViewGroup。
    • TouchEvent事件处理相关的方法:
      dispatchTouchEvent(MotionEvent event)
      onInterceptTouchEvent(MotionEvent ev)
      onTouchEvent(MotionEvent event)
  4. Activity
    • Activity是Android四大基本组件之一,这里只介绍Activity与touch相关的方法。当手指触摸到屏幕时,屏幕硬件一行行不断地扫描每个像素点,获取到触摸事件后,从底层产生中断上报。再通过native层调用Java层InputEventReceiver中的dispatchInputEvent方法。经过层层调用,交由Activity的dispatchTouchEvent方法来处理。
    • TouchEvent事件处理相关的方法:
      dispatchTouchEvent(MotionEvent event)
      onTouchEvent(MotionEvent event)

流程图

当用户触摸屏幕时会产生一系列MotionEvent,那么Activity,ViewGroup,View之间是如何处理呢?这里通过一张图来从全局性描述整个流程

这里写图片描述

结论

流程图总结

  • onInterceptTouchEvent返回值true表示事件拦截, onTouch/onTouchEvent 返回值true表示事件消费。

  • 触摸事件先交由Activity.dispatchTouchEvent。再一层层往下分发,当中间的ViewGroup都不拦截时,进入最底层的View后,开始由最底层的OnTouchEvent来处理,如果一直不消费,则最后返回到Activity.OnTouchEvent。

  • ViewGroup才有onInterceptTouchEvent拦截方法。在分发过程中,中间任何一层ViewGroup都可以直接拦截,则不再往下分发,而是交由发生拦截操作的ViewGroup的OnTouchEvent来处理。

  • 子View可调用requestDisallowInterceptTouchEvent方法,来设置disallowIntercept=true,从而阻止父ViewGroup的onInterceptTouchEvent拦截操作。

  • OnTouchEvent由下往上冒泡时,当中间任何一层的OnTouchEvent消费该事件,则不再往上传递,表示事件已处理。

  • 如果View没有消费ACTION_DOWN事件,则之后的ACTION_MOVE等事件都不会再接收。

  • 只要View.onTouchEvent是可点击或可长按,则消费该事件.

  • onTouch优先于onTouchEvent执行,上面流程图中省略,onTouch的位置在onTouchEvent前面。当onTouch返回true,则不执行onTouchEvent,否则会执行onTouchEvent。onTouch只有View设置了OnTouchListener,且是enable的才执行该方法。

区分总结

  1. View触摸事件传递机制:

    • 触摸控件(View)首先执行dispatchTouchEvent方法。
    • 在dispatchTouchEvent方法中先执行onTouch方法,后执行onClick方法
    • 如果控件(View)的onTouch返回false或者mOnTouchListener为null(控件没有设置setOnTouchListener方法)或者控件不是enable的情况下会调运onTouchEvent,dispatchTouchEvent返回值与onTouchEvent返回一样。
    • 如果控件不是enable的设置了onTouch方法也不会执行,只能通过重写控件的onTouchEvent方法处理(上面已经处理分析了),dispatchTouchEvent返回值与onTouchEvent返回一样。
    • 如果控件(View)是enable且onTouch返回true情况下,dispatchTouchEvent直接返回true,不会调用onTouchEvent方法。
    • 当dispatchTouchEvent在进行事件分发的时候,只有前一个action返回true,才会触发下一个action(也就是说dispatchTouchEvent返回true才会进行下一次action派发)。
  2. ViewGroup触摸事件传递机制:

    • Android事件派发是先传递到最顶级的ViewGroup,再由ViewGroup递归传递到View的。
    • 在ViewGroup中可以通过onInterceptTouchEvent方法对事件传递进行拦截,onInterceptTouchEvent方法返回true代表不允许事件继续向子View传递,返回false代表不对事件进行拦截,默认返回false。
    • 子View中如果将传递的事件消费掉,ViewGroup中将无法接收到任何事件。
  3. Activity触摸事件传递机制:

    • 首先会触发Activity的dispatchTouchEvent方法
    • dispatchTouchEvent方法中如果是ACTION_DOWN的情况下会接着触发onUserInteraction方法。
    • 接着在dispatchTouchEvent方法中会通过Activity的root View(id为content的FrameLayout),实质是ViewGroup,通过super.dispatchTouchEvent把touchevent派发给各个activity的子view,也就是我们再Activity.onCreat方法setContentView时设置的view。
    • 若Activity下面的子view拦截了touchevent事件(返回true)则Activity.onTouchEvent方法就不会执行

最后

  • 附上我的测试源码地址:https://github.com/myloften/ViewEventSample/tree/master

  • 如需查看更详细的源码分析,请看以下博客分析:
    Android触摸屏事件派发机制详解与源码分析一(View篇)
    Android触摸屏事件派发机制详解与源码分析二(ViewGroup篇)
    Android触摸屏事件派发机制详解与源码分析三(Activity篇)
    Android事件分发机制

1 0
原创粉丝点击