Android的触摸消息中,已经实现了三种监测,它们分别是

1)pre-pressed:对应的语义是用户轻触(tap)了屏幕

2)pressed:对应的语义是用户点击(press)了屏幕

3)long pressed:对应的语义是用户长按(long press)了屏幕

下图是触摸消息随时间变化的时间轴示意图:

其中,t0和t1定义在ViewConfiguration类中,标识了tap和longpress的超时时间,定义如下:

  1. /**
  2. * Defines the duration in milliseconds we will wait to see if a touch event
  3. * is a tap or a scroll. If the user does not move within this interval, it is
  4. * considered to be a tap.
  5. */
  6. private static final int TAP_TIMEOUT = 115; // t0
  7. /**
  8. * Defines the duration in milliseconds before a press turns into
  9. * a long press
  10. */
  11. private static final int LONG_PRESS_TIMEOUT = 500; // t1

代码中实现监测的地方在View类的OnTouchEvent函数中,当View监测到ACTION_DOWN事件时,首先发送一个延迟为t0的异步消息,代码如下:

  1. case MotionEvent.ACTION_DOWN:
  2. if (mPendingCheckForTap == null) {
  3. mPendingCheckForTap = new CheckForTap();
  4. }
  5. mPrivateFlags |= PREPRESSED;
  6. mHasPerformedLongPress = false;
  7. postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
  8. break;

如果在t0时间内用户释放了屏幕,即ACTION_UP事件在t0时间内发生,则本次触摸对应的是pre-pressed处理代码,语义是"用户轻触(TAP)了一下屏幕";如果用户在t1时间内释放了屏幕,那么本次操作是一个"press"操作;如果用户超过t1时间释放屏幕,则系统认为监测到了长按事件。其中处理"press"操作的代码在类CheckForTap类中,处理长按操作的代码在类CheckForLongPress类中。而处理pre-pressed的代码在ACTION_UP事件响应中,ACTION_UP事件响应中大部分代码用于处理触摸的状态变化,如下所示:

  1. case MotionEvent.ACTION_UP:
  2. boolean prepressed = (mPrivateFlags & PREPRESSED) != 0; //获取prepressed状态
  3. if ((mPrivateFlags & PRESSED) != 0 || prepressed) { //如果是pressed状态或者是prepressed状态,才进行处理
  4. // 如果当前view不具有焦点,则需要先获取焦点,因为我们当前处理触摸模式
  5. boolean focusTaken = false;
  6. if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
  7. focusTaken = requestFocus(); // 请求获得焦点
  8. }
  9. if (!mHasPerformedLongPress) { // 是否处理过长按操作了,如果是,则直接返回
  10. // 进入该代码段,说明这是一个tap操作,首先移除长按回调操作
  11. removeLongPressCallback();
  12. // Only perform take click actions if we were in the pressed state
  13. if (!focusTaken) {
  14. // Use a Runnable and post this rather than calling
  15. // performClick directly. This lets other visual state
  16. // of the view update before click actions start.
  17. if (mPerformClick == null) {
  18. mPerformClick = new PerformClick();
  19. }
  20. if (!post(mPerformClick)) {
  21. performClick(); // 执行点击的处理函数
  22. }
  23. }
  24. }
  25. if (mUnsetPressedState == null) {
  26. mUnsetPressedState = new UnsetPressedState();
  27. }
  28. if (prepressed) {
  29. mPrivateFlags |= PRESSED;
  30. refreshDrawableState();
  31. // 发送重置触摸状态的异步延迟消息
  32. postDelayed(mUnsetPressedState,
  33. ViewConfiguration.getPressedStateDuration());
  34. } else if (!post(mUnsetPressedState)) {
  35. // If the post failed, unpress right now
  36. mUnsetPressedState.run();
  37. }
  38. removeTapCallback(); // 移除tap的回调操作
  39. }
  40. break;

在上面的代码分析中,可以看出,整个监测过程涉及到两个Runnable对象和一个利用Handler发送异步延迟消息的函数,下面就来分析一下:

1)PostDelayed函数

该函数的主要工作是获取UI线程的Handler对象,然后调用Handler类的postDelayed函数将指定的Runnable对象放到消息队列中。

  1. public boolean postDelayed(Runnable action, long delayMillis) {
  2. Handler handler;
  3. if (mAttachInfo != null) {
  4. handler = mAttachInfo.mHandler;
  5. } else {
  6. // Assume that post will succeed later
  7. ViewRoot.getRunQueue().postDelayed(action, delayMillis);
  8. return true;
  9. }
  10. return handler.postDelayed(action, delayMillis);
  11. }

2)CheckForTap类

该类实现了Runnable接口,在run函数中设置触摸标识,并刷新Drawable的状态,同时用于发送一个检测长按事件的异步延迟消息,代码如下:

  1. private final class CheckForTap implements Runnable {
  2. public void run() {
  3. // 进入该函数,说明已经过了ViewConfiguration.getTapTimeout()时间,
  4. // 即pre-pressed状态结束,宣告触摸进入pressed状态
  5. mPrivateFlags &= ~PREPRESSED;
  6. mPrivateFlags |= PRESSED;
  7. refreshDrawableState(); // 刷新控件的背景Drawable
  8. // 如果长按检测没有被去使能,则发送一个检测长按事件的异步延迟消息
  9. if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
  10. postCheckForLongClick(ViewConfiguration.getTapTimeout());
  11. }
  12. }
  13. }
  14. private void postCheckForLongClick(int delayOffset) {
  15. mHasPerformedLongPress = false;
  16. // 实例化CheckForLongPress对象
  17. if (mPendingCheckForLongPress == null) {
  18. mPendingCheckForLongPress = new CheckForLongPress();
  19. }
  20. mPendingCheckForLongPress.rememberWindowAttachCount();
  21. // 调用PostDelayed函数发送长按事件的异步延迟消息
  22. postDelayed(mPendingCheckForLongPress,
  23. ViewConfiguration.getLongPressTimeout() - delayOffset);
  24. }

3)CheckForLongPress类

该类定义了长按操作发生时的响应处理,同样实现了Runnable接口

  1. public boolean postDelayed(Runnable action, long delayMillis) {
  2. Handler handler;
  3. if (mAttachInfo != null) {
  4. handler = mAttachInfo.mHandler;
  5. } else {
  6. // Assume that post will succeed later
  7. ViewRoot.getRunQueue().postDelayed(action, delayMillis);
  8. return true;
  9. }
  10. return handler.postDelayed(action, delayMillis);
  11. }

2)CheckForTap类

该类实现了Runnable接口,在run函数中设置触摸标识,并刷新Drawable的状态,同时用于发送一个检测长按事件的异步延迟消息,代码如下:

  1. private final class CheckForTap implements Runnable {
  2. public void run() {
  3. // 进入该函数,说明已经过了ViewConfiguration.getTapTimeout()时间,
  4. // 即pre-pressed状态结束,宣告触摸进入pressed状态
  5. mPrivateFlags &= ~PREPRESSED;
  6. mPrivateFlags |= PRESSED;
  7. refreshDrawableState(); // 刷新控件的背景Drawable
  8. // 如果长按检测没有被去使能,则发送一个检测长按事件的异步延迟消息
  9. if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
  10. postCheckForLongClick(ViewConfiguration.getTapTimeout());
  11. }
  12. }
  13. }
  14. private void postCheckForLongClick(int delayOffset) {
  15. mHasPerformedLongPress = false;
  16. // 实例化CheckForLongPress对象
  17. if (mPendingCheckForLongPress == null) {
  18. mPendingCheckForLongPress = new CheckForLongPress();
  19. }
  20. mPendingCheckForLongPress.rememberWindowAttachCount();
  21. // 调用PostDelayed函数发送长按事件的异步延迟消息
  22. postDelayed(mPendingCheckForLongPress,
  23. ViewConfiguration.getLongPressTimeout() - delayOffset);
  24. }

3)CheckForLongPress类

该类定义了长按操作发生时的响应处理,同样实现了Runnable接口

  1. class CheckForLongPress implements Runnable {
  2. private int mOriginalWindowAttachCount;
  3. public void run() {
  4. // 进入该函数,说明检测到了长按操作
  5. if (isPressed() && (mParent != null)
  6. && mOriginalWindowAttachCount == mWindowAttachCount) {
  7. if (performLongClick()) {
  8. mHasPerformedLongPress = true;
  9. }
  10. }
  11. }
  12. public void rememberWindowAttachCount() {
  13. mOriginalWindowAttachCount = mWindowAttachCount;
  14. }
  15. }
  16. public boolean performLongClick() {
  17. sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
  18. boolean handled = false;
  19. if (mOnLongClickListener != null) {
  20. // 回调用户实现的长按操作监听函数(OnLongClickListener)
  21. handled = mOnLongClickListener.onLongClick(View.this);
  22. }
  23. if (!handled) {
  24. // 如果OnLongClickListener的onLongClick返回false
  25. // 则需要继续处理该长按事件,这里是显示上下文菜单
  26. handled = showContextMenu();
  27. }
  28. if (handled) {
  29. // 长按操作事件被处理了,此时应该给用户触觉上的反馈
  30. performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
  31. }
  32. return handled;
  33. }

Android系统中长按事件的实现机制解析的更多相关文章

  1. [学习总结]4、Android的ViewGroup中事件的传递机制(一)

    本文主要针对dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent三个方法,通过简单的例子来简单的介绍下. 根据字面意思的理解,dispatchTo ...

  2. [学习总结]5、Android的ViewGroup中事件的传递机制(二)

    下面是第一篇的连接 Android的ViewGroup中事件的传递机制(一) 关于onInterceptTouchEvent和onTouchEvent的详细解释. 1 public class Mai ...

  3. Android系统中自定义按键的短按、双击、长按事件

    在项目中碰到这样的问题: 由于系统中的按键在底层做了重新定义或者新增了按键,此时需要在APP层对按键事件(keyevent)做分解处理,模拟Android系统做法,把keyevent分解成: 1.单击 ...

  4. Android系统中的广播(Broadcast)机制简要介绍和学习计划

    在Android系统中,广播(Broadcast)是在组件之间传播数据(Intent)的一种机制:这些组件甚至是可以位于不同的进程中,这样它就像Binder机制一样,起到进程间通信的作用:本文通过一个 ...

  5. Android系统init进程启动及init.rc全解析

    转:https://blog.csdn.net/zhonglunshun/article/details/78615980 服务启动机制system/core/init/init.c文件main函数中 ...

  6. Android系统匿名共享内存Ashmem(Anonymous Shared Memory)驱动程序源代码分析

    文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6664554 在上一文章Android系统匿名共 ...

  7. 【转】Android系统开篇

    版权声明:本站所有博文内容均为原创,转载请务必注明作者与原文链接,且不得篡改原文内容.另外,未经授权文章不得用于任何商业目的. 一.引言 Android系统非常庞大.错综复杂,其底层是采用Linux作 ...

  8. Android Touch事件传递机制解析 (推荐)

    最近新闻列表里的下拉 down up  move 等等让我十分头疼 ,无意间看到了一篇非常不错的帖子,转载如下: 开篇语:最近程序在做一个小效果,要用到touch,结果整得云里面雾里的,干脆就好好把a ...

  9. Android Touch事件传递机制解析

    android系统中的每个ViewGroup的子类都具有下面三个和TouchEvent处理密切相关的方法: 1)public boolean dispatchTouchEvent(MotionEven ...

随机推荐

  1. 你好,C++(14)如何描述“一个名叫陈良乔,年龄33岁,身高173厘米,体重61.5千克的男人”——3.8 用结构体类型描述复杂的事物

    3.8  用结构体类型描述复杂的事物 利用C++本身所提供的基本数据类型所定义的变量,只能表达一些简单的事物.比如我们可以用int类型定义nAge变量表示人的年龄,用string类型定义strName ...

  2. DSP TMS320C6000基础学习(6)—— gel文件

    什么是gel文件?gel文件能干什么? gel全称General Extended Language,即通用扩展语言文件,gel文件中由类似C语言的代码构成,gel语言是一种解释性语言,gel文件扩展 ...

  3. CSS代码写出的各种形状图形

    做网页设计时经常要用到各种形状的图形,对于规则的图形很简单,但是对于不规则的图形,一般我们都是用图片,今天就在这里教大家怎样用css代码写出各种规则不同的图形 1.正方形 #square {width ...

  4. centos 下 apache 重启启动命令

    apache 启动 usr/local/apache243/bin/apachectl start apache 重启 usr/local/apache243/bin/apachectl restar ...

  5. 不在界面上用控件 动态创建idhttp,IdAntiFreeze来用

    不在界面上用控件 动态创建idhttp,IdAntiFreeze来用 var IdHTTP: Tidhttp; IdAntiFreeze: TidAntiFreeze; begin IdAntiFre ...

  6. 使用Jstl异常:The absolute uri: http://java.sun.com/jsp/jstl/core cannot&nbs

    错误提示是:        org.apache.jasper.JasperException: This absolute uri http://java.sun.com/jsp/jstl/core ...

  7. codevs 1031 质数环

    题目描述 Description 一个大小为N(N<=17)的质数环是由1到N共N个自然数组成的一个数环,数环上每两个相邻的数字之和为质数.如下图是一个大小为6的质数环.为了方便描述,规定数环上 ...

  8. Android json操作之取得一个对象

    1:服务端返回的json数据格式如下: {"id":"1001","name":"zhangsan","sco ...

  9. DES加解密实现方式

    private static readonly byte[] _keys = { 0x22, 0x84, 0x56, 0x98, 0x90, 0xAB, 0xpD, 0xEF }; private s ...

  10. 是用VLC API将RTSP流convert为视频文件

    一直在文档中找不到,接口设计的也不大好.不过终于在stackoverflow上找到解决方案了. 原来在LIbVLC库实例化的时候就需要传递相关的参数.我的代码大致是这样实现: const char * ...