前两天想研究下Android点击事件是如何处理的,翻译了一篇国外文件,英文原版下载地址附在文后。

 

Android怎样处理点击事件

Android的点击事件封装在MotionEvent中,点击事件可以分为以下几种:

 - ACTION_DOWN

 - ACTION_UP

 - ACTION_MOVE

 - ACTION_POINTER_DOWN

 - ACTION_POINTER_UP

 - ACTION_CANCEL

点击事件的数据包括:

 - Touch loaction

 - Number of pointers (fingers)

 - Event time

一次点击事件起始于ACTION_DOWN,结束于ACTION_UP。

最先对点击事件进行响应的是Activity中的dispatchTouchEvent()回调,并且从父视图向子视图依次传递,在传递过程中可以随时对点击事件进行拦截处理。

点击事件自顶向下传递并逆向返回,直至点击事件被处理,在View中必须通过处理ACTION_DOWN动作来响应点击事件;为了提高效率,一旦点击事件被处理,则不会继续向下传递。

任何未被处理的点击事件最终都会返回到Activity中的onTouchEvent()回调函数,并于此结束。

可以在任何View/ViewGroup中通过使用可选的外部OnTouchListener来截取点击事件。

 

Activity.dispatchTouchEvent():

此回调通常是最先被调用的,并向关联于当前用户界面的根视图(root view)发送点击事件。

 - onTouchEvent()

    如果没有视图(view)处理点击事件,此回调将被调用,通常是最后才被调用的。

 

View.dispatchTouchEvent():

此回调首先向监听器(listener)发送点击事件,如果存在监听器,则调用回调View.OnTouchListener.onTouch();如果点击事件没有被处理,则通过回调View.onTouchEvent()来处理点击事件。

 

ViewGroup.dispatchTouchEvent():

 - onInterceptTouchEvent()

    此函数的功能:

        - 检查是否需要取代子视图

        - 向活动的子视图传递ACTION_CANCEL事件

        - 一旦子序列中的事件全部处理完毕返回true值

 - 对各个子视图按照添加的顺序进行反转

        - 如果点击事件是相关的(在view视图内),调用child.dispatchTouchEvent()

        - 如果点击事件没有被前一个视图处理掉,分发给下一个视图。

 - 如果没有子视图处理监听事件,监听器得到了通过下面回调处理点击事件的机会:

        OnTouchListener.onTouch()

 - 如果没有监听器,或者点击事件没有被处理,调用下面的函数:

        onTouchEvent()

被拦截的点击事件会停止向子视图传递

 

Android click点击事件传递 android点击事件处理_监听器

 

Android click点击事件传递 android点击事件处理_点击事件_02

 

Android click点击事件传递 android点击事件处理_监听器_03

 




通常的点击事件处理方式

处理点击事件可通过子类重写onTouchEvent()方法和提供OnTouchListener监听器来实现。

 

销毁点击事件

    - Return true with ACTION_DOWN to show interest

        即使对ACTION_DOWN不感兴趣,也要返回true。

    - 对于其他事件,返回true会简单地停止进一步的处理。

 

ViewConfiguration中可用的有用常量

    - getScaledTouchSlop()

        Distance  move events might vary before they should be considered a drag

        位移事件在被认定为拖拽前可能存在变数

    - getScaledMinimumFlingVelocity()

        用来获取点击移动速度,系统据此判断是慢速拖拽还是快速拖拽

    - getLongPressTimeout()

        系统据此获取的数据判断是否是长点击事件

    - 根据每个设备的分辨率显示不同的数值

 

转发点击事件

    - 调用目标视图的dispatchTouchEvent()

    - 避免直接调用目标视图的onTouchEvent()

 

偷取点击事件

    - 子类重写onInterceptTouchEvent()

    - 当你要接管点击处理事件时返回true

        所有针对当前动作的子序列点击事件将直接进入到onTouchEvent()回调中,将不再为每一个事件调用onInterceptTouchEvent()回调函数

    - 任何当前目标视图均会接收到ACTION_CANCEL

 

用户点击事件处理警告

在任何需要的时候调用上层处理方法

    - View.onTouchEvent()对点击事件进行了大量的状态管理(pressed,checked,etc.),如果自己抓取每个点击事件将会遗漏许多内容。

 

通过溢出检测(slop check)进行ACTION_MOVE保护

    - 避免手指接触面积过大或手指滑动

 

一直处理ACTION_CANCEL事件

    - 有用户操作动作(如滚动)的视图容器会盗取点击事件,需要对状态进行重置。

    - 牢记进行CANCEL操作后将不会收到任何其他消息。

 

不要对点击事件进行截取除非你准备对他们进行处理

    - 截取点击事件是不可逆的

 

多点点击事件处理

MotionEvent.getPointerCount()

    - 获取当前屏幕上的触点个数

 

使用ACTION_POINTER_DOWN和ACTION_POINTER_UP事件来检测第二触点

    - MotionEvent.getActionMasked()

    - MotionEvent.getActionIndex()

 

使用MotionEvent方法从触点列表参数中获取特定的触点数据

    - 方法中不传递参数通常返回第一个触点的数据

 

批处理

为了提高效率,ACTION_MOVE事件可以在一个MotionEvent内进行批次处理

最新的点击事件通常可以通过如下的标准方法获取到相应数据:

    - getX(), getY(), getEventTime()

发生于当前和上一个ACTION_MOVE之间的点击事件可以通过以下获取历史数据的方法获得:

    -getHistoricalX(), getHistoricalY(), getHistoricalEventTime()

    - getHistoricalSize()方法返回进行批处理点击事件的个数

当所有的点击事件在时间上具有最大精度时可以对所有点击事件进行重构

 

系统点击处理程序

如果没有必要,不用立即跳转到自定义点击处理程序。

OnClickListener

OnLongClickListener

OnTouchListener

OnTouchListener

    - 监控个别的没有子类的MotionEvent

    - 可以在监听器中处理点击事件

    - 可以事先清空视图的处理器

OnScrollListener/View.onScrollChanged()

    - 处理具有滚动功能的视图发生了滚动事件

 

处理更复杂的点击交互

手势检测器

    - onDown(), onSingleTapUp(), onDoubleTap()

    - onLongPress()

    - onScroll() (用户用手机拖拽)

    - onFling() (用户快速结束拖拽)

平衡手势检测器

    - onScaleBegin(), onScale(), onScaleEnd()

通过OnTouchListener()或onTouchEvent()处理