zoukankan      html  css  js  c++  java
  • Android触摸事件传递机制

        简单梳理一下Android触摸事件传递机制的知识点。

    一、View与ViewGroup的关系

        View和ViewGroup二者的继承关系如下图所示:

        View是Android中最基本的一种UI组件,它是所有控件类的基类。View类的作用是可以在屏幕上绘制一块矩形区域,并能响应这块区域的各种事件(如触摸事件、点击事件等)。我们平时使用的各种控件其实都是继承自View类,在View的基础上又添加了一些特有的功能。比如TextView可以用于显示文本,进一步还能拓展(extends)为可编辑的文本控件——EditText类或者可点击的文本控件——Button类。

        ViewGroup也是继承自View类,但它是一种非常特殊的View,因为它可以作为一个容器来放置其他的控件或布局。我们常用的各种布局Layout类以及ListView、ScrollView等都是继承自ViewGroup。

        在讨论Android触摸事件传递机制时,我们这里说的View特指除了ViewGroup以外的View控件,也就是无法作为容器的最小UI单位。

    二、Android UI层次结构

        Android UI层次结构图和类图如下图所示:

     

                                                                                           

         

      Activity是Android应用程序的门面和载体,它代表一个完整的用户界面。Activity提供了一个窗口来绘制各种视图,即PhoneWindow类。该类继承自顶层窗口类Window,并且包含一个DecorView类对象。DecorView继承自FrameLayout(帧布局),所以本质上是一个ViewGroup,而且是当前活动所放置的全部View的根视图(RootView)。当我们创建一个活动时,在活动的onCreate()方法中调用setContentView(R.layout.layout_name)方法就是为该活动的ContentView部分指定布局内容从而完成GUI的渲染。

    三、事件的类型

        事件主要分为触摸事件和点击事件。

        1、触摸事件:对应的是MotionEvent类,主要有以下三种类型:

        ACTION_DOWN:表示用户手指按下的动作,标志着触摸事件的开始。

        ACTION_UP:表示用户手指离开屏幕的动作,标志着触摸事件的结束。

        ACTION_MOVE:表示用户手指移动的动作。当用户手指按下屏幕后,在松开之前,只要移动的距离超过了一定的阈值即判定为ACTION_MOVE动作。实际上,即使是手指非常   轻微的移动也会被系统监测到从而判定为ACTION_MOVE动作。

        ps:用户触摸屏幕操作由ACTION_DOWN事件开始,结束于ACTION_UP事件,可以有0次或多次ACTION_MOVE事件。

     

        2、点击事件:用户手指按下→停留若干时间(可长可短)→用户手指松开,这一完整的过程视为一次点击事件。可以看出,触摸事件先于点击事件执行。

    四、触摸事件传递机制

        该机制主要包含三个角色、三个阶段和三个方法。

        1、三个角色分别为:Activity、View、ViewGroup。

        2、三个阶段分别为:分发(dispatch)、拦截(intercept)和消费(consume)。

        3、三个方法的方法原型分别为:

        ①public boolean dispatchTouchEvent(MotionEvent e)……对应于分发事件

        ②public boolean onInterceptTouchEvent(MotionEvent e)……对应于拦截事件

       ③public boolean onTouchEvent(MotionEvent e)……对应于消费事件(即处理事件)

        4、解释:

        由前面的Android UI层次结构分析可以看出来,一个Activity包含多层视图:先是窗口(PhoneWindow),接着是根视图(即DecorView),然后是具体的ContentView;ContentView视图部分又是各种布局和控件的嵌套组合。那么,当用户做出了触摸动作时,触摸事件应当由哪一个View或者哪一个ViewGroup负责处理呢?这就是触摸事件传递机制要解决的问题。为了方便,下面会用视图这一词代替View或ViewGroup对象。

        ①在Android系统中,所有触摸事件必须都由dispatchTouchEvent()方法判断是否进行分发。分发事件相当于“传递事件”的一个过程,分发事件的目的就是为了找到正确的视图来处理这个触摸事件。而处理事件的过程就是所谓的消费阶段,调用的方法是onTouchEvent()。如果确定要继续分发事件,向什么地方分发呢?如果是ViewGroup,则是向自己的子视图进行分发;如果是View,向子视图进行分发本质上其实就是分发给自身。如果dispatchTouchEvent()方法返回true,则表示事件由当前视图直接进行消费(处理),不再继续分发;否则,向子视图进行分发。

        ②除了分发和消费,还有一个拦截操作。须注意的是:只有ViewGroup可以拦截触摸事件,Activity和View只能分发事件和消费事件。这一点其实非常好理解:因为View没有子视图可言,所以不存在拦截的操作;而ViewGroup具有子视图,选择性地进行拦截事件是有意义的。ViewGroup可以调用onInterceptTouchEvent()方法来判断是否拦截该触摸事件。如果该方法返回true,表示拦截该事件不再分发给子视图;否则,表示不拦截事件继续分发给子视图。须注意,ViewGroup类默认是不拦截任何触摸事件的,如果有需要可以重写其onInterceptTouchEvent()方法进行选择性地拦截。

        ③一旦确定由某个视图负责处理(即消费)触摸事件ACTION_DOWN,那么后续的ACTION_MOVE和ACTION_UP等触摸事件也会同样被该视图捕获。在消费事件阶段,该视图会调用onTouchEvent()方法。如果该方法返回true,那么表示事件被成功消费;如果返回false,那么意味着当前视图没有能力消费该触摸事件,事件会抛给上层的视图调用其onTouchEvent()方法进行处理。

     

        5、触摸事件传递过程:

        考虑最一般的情况,即触摸事件ACTION_DOWN由某个View消费掉的情况。事件传递过程分成两步,整个过程都严格遵循上述的分发、拦截和消费的原理。如下图所示:

                                       

      

        ①第一步:先由Activity传递到ViewGroup,具体地讲就是Activity→PhoneWindow→DecorView(根视图)。

        ②第二步:由根视图DecorView通过一层层的ViewGroup传递到某个子View,由该View负责消费该事件以及后续的事件。

        至于其他的情况,比如该View捕获到事件后无法消费抛给上层视图或者某个ViewGroup捕获到事件,则参考下图:

     

     

    五、something more

        除了分发、拦截和消费这三个方法外,还可以在活动中为View类设置监听器接口:

        监听触摸事件:setOnTouchListener(),须重写接口的onTouch()方法。

        监听点击事件:setOnClickListener(),须重写接口的onClick()方法。

        监听长按事件:setOnLongClickListener(),须重写接口的onLongClick()方法

        观察View类的源码,有以下结论:

        ①View的onTouch()方法会先于onTouchEvent()方法执行,并且当前仅当onTouch()方法返回false时,才会执行onTouchEvent()。

        ②只有当onTouchEvent()方法得到调用,并且触摸事件是ACTION_UP时,onClick()方法才会被调用。这一点逻辑上也非常容易理解,因为当用户手指按下屏幕,只有当他手指离开屏幕触发事件ACTION_UP后才算是一个点击事件。也就是说,当一个View确定捕获触摸事件时,onTouch()方法会介于分发方法和消费方法之间执行,并且只有当其返回false时,后续的消费方法onTouchEvent()才能执行。而只有onTouchEvent()方法成功执行返回true,才能表示触摸事件被正确处理,如果是ACTION_UP被正确处理,那么就会调用onClick()方法。

        ③这三个监听方法的执行顺序是:onTouch()方法→onLongClick()→onClick() 原因:当用户按下手指后,先执行触摸监听方法;如果用户按下时间超过一个阈值还没有松手,会执行长按监听方法;如果用户最后松开手指,最后执行点击监听方法。并且这三个方法是后者依赖于前者的返回值!只有onClick()返回false,onLongClick()才能执行;只有onLongClick()返回false,onClick()才能执行。

    .............................................................................................................................................................over

     

  • 相关阅读:
    php单引号和双引号
    转CSS技巧大全
    在C#中实现打印功能(C#中PrintDialog,PrintDocument的使用) (转)
    从安装到使用Ubuntu遇到问题解决问题一览
    原型模型
    PHP源代码分析 tick(s)
    在DOS下修改windows 登入密码 转载
    配置php5.3.6+Apache2.2
    Java多线程全源码分析
    本笨鸟今天开始学习.net,有木有一起开始学的
  • 原文地址:https://www.cnblogs.com/shakinghead/p/10685148.html
Copyright © 2011-2022 走看看