Android系统中长按事件的实现机制解析
在Android的触摸消息中,已经实现了三种监测,它们分别是
1)pre-pressed:对应的语义是用户轻触(tap)了屏幕
2)pressed:对应的语义是用户点击(press)了屏幕
3)long pressed:对应的语义是用户长按(long press)了屏幕
下图是触摸消息随时间变化的时间轴示意图:
其中,t0和t1定义在ViewConfiguration类中,标识了tap和longpress的超时时间,定义如下:
- /**
- * Defines the duration in milliseconds we will wait to see if a touch event
- * is a tap or a scroll. If the user does not move within this interval, it is
- * considered to be a tap.
- */
- private static final int TAP_TIMEOUT = 115; // t0
- /**
- * Defines the duration in milliseconds before a press turns into
- * a long press
- */
- private static final int LONG_PRESS_TIMEOUT = 500; // t1
代码中实现监测的地方在View类的OnTouchEvent函数中,当View监测到ACTION_DOWN事件时,首先发送一个延迟为t0的异步消息,代码如下:
- case MotionEvent.ACTION_DOWN:
- if (mPendingCheckForTap == null) {
- mPendingCheckForTap = new CheckForTap();
- }
- mPrivateFlags |= PREPRESSED;
- mHasPerformedLongPress = false;
- postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
- break;
如果在t0时间内用户释放了屏幕,即ACTION_UP事件在t0时间内发生,则本次触摸对应的是pre-pressed处理代码,语义是"用户轻触(TAP)了一下屏幕";如果用户在t1时间内释放了屏幕,那么本次操作是一个"press"操作;如果用户超过t1时间释放屏幕,则系统认为监测到了长按事件。其中处理"press"操作的代码在类CheckForTap类中,处理长按操作的代码在类CheckForLongPress类中。而处理pre-pressed的代码在ACTION_UP事件响应中,ACTION_UP事件响应中大部分代码用于处理触摸的状态变化,如下所示:
- case MotionEvent.ACTION_UP:
- boolean prepressed = (mPrivateFlags & PREPRESSED) != 0; //获取prepressed状态
- if ((mPrivateFlags & PRESSED) != 0 || prepressed) { //如果是pressed状态或者是prepressed状态,才进行处理
- // 如果当前view不具有焦点,则需要先获取焦点,因为我们当前处理触摸模式
- boolean focusTaken = false;
- if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
- focusTaken = requestFocus(); // 请求获得焦点
- }
- if (!mHasPerformedLongPress) { // 是否处理过长按操作了,如果是,则直接返回
- // 进入该代码段,说明这是一个tap操作,首先移除长按回调操作
- removeLongPressCallback();
- // Only perform take click actions if we were in the pressed state
- if (!focusTaken) {
- // Use a Runnable and post this rather than calling
- // performClick directly. This lets other visual state
- // of the view update before click actions start.
- if (mPerformClick == null) {
- mPerformClick = new PerformClick();
- }
- if (!post(mPerformClick)) {
- performClick(); // 执行点击的处理函数
- }
- }
- }
- if (mUnsetPressedState == null) {
- mUnsetPressedState = new UnsetPressedState();
- }
- if (prepressed) {
- mPrivateFlags |= PRESSED;
- refreshDrawableState();
- // 发送重置触摸状态的异步延迟消息
- postDelayed(mUnsetPressedState,
- ViewConfiguration.getPressedStateDuration());
- } else if (!post(mUnsetPressedState)) {
- // If the post failed, unpress right now
- mUnsetPressedState.run();
- }
- removeTapCallback(); // 移除tap的回调操作
- }
- break;
在上面的代码分析中,可以看出,整个监测过程涉及到两个Runnable对象和一个利用Handler发送异步延迟消息的函数,下面就来分析一下:
1)PostDelayed函数
该函数的主要工作是获取UI线程的Handler对象,然后调用Handler类的postDelayed函数将指定的Runnable对象放到消息队列中。
- public boolean postDelayed(Runnable action, long delayMillis) {
- Handler handler;
- if (mAttachInfo != null) {
- handler = mAttachInfo.mHandler;
- } else {
- // Assume that post will succeed later
- ViewRoot.getRunQueue().postDelayed(action, delayMillis);
- return true;
- }
- return handler.postDelayed(action, delayMillis);
- }
2)CheckForTap类
该类实现了Runnable接口,在run函数中设置触摸标识,并刷新Drawable的状态,同时用于发送一个检测长按事件的异步延迟消息,代码如下:
- private final class CheckForTap implements Runnable {
- public void run() {
- // 进入该函数,说明已经过了ViewConfiguration.getTapTimeout()时间,
- // 即pre-pressed状态结束,宣告触摸进入pressed状态
- mPrivateFlags &= ~PREPRESSED;
- mPrivateFlags |= PRESSED;
- refreshDrawableState(); // 刷新控件的背景Drawable
- // 如果长按检测没有被去使能,则发送一个检测长按事件的异步延迟消息
- if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
- postCheckForLongClick(ViewConfiguration.getTapTimeout());
- }
- }
- }
- private void postCheckForLongClick(int delayOffset) {
- mHasPerformedLongPress = false;
- // 实例化CheckForLongPress对象
- if (mPendingCheckForLongPress == null) {
- mPendingCheckForLongPress = new CheckForLongPress();
- }
- mPendingCheckForLongPress.rememberWindowAttachCount();
- // 调用PostDelayed函数发送长按事件的异步延迟消息
- postDelayed(mPendingCheckForLongPress,
- ViewConfiguration.getLongPressTimeout() - delayOffset);
- }
3)CheckForLongPress类
该类定义了长按操作发生时的响应处理,同样实现了Runnable接口
- public boolean postDelayed(Runnable action, long delayMillis) {
- Handler handler;
- if (mAttachInfo != null) {
- handler = mAttachInfo.mHandler;
- } else {
- // Assume that post will succeed later
- ViewRoot.getRunQueue().postDelayed(action, delayMillis);
- return true;
- }
- return handler.postDelayed(action, delayMillis);
- }
2)CheckForTap类
该类实现了Runnable接口,在run函数中设置触摸标识,并刷新Drawable的状态,同时用于发送一个检测长按事件的异步延迟消息,代码如下:
- private final class CheckForTap implements Runnable {
- public void run() {
- // 进入该函数,说明已经过了ViewConfiguration.getTapTimeout()时间,
- // 即pre-pressed状态结束,宣告触摸进入pressed状态
- mPrivateFlags &= ~PREPRESSED;
- mPrivateFlags |= PRESSED;
- refreshDrawableState(); // 刷新控件的背景Drawable
- // 如果长按检测没有被去使能,则发送一个检测长按事件的异步延迟消息
- if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
- postCheckForLongClick(ViewConfiguration.getTapTimeout());
- }
- }
- }
- private void postCheckForLongClick(int delayOffset) {
- mHasPerformedLongPress = false;
- // 实例化CheckForLongPress对象
- if (mPendingCheckForLongPress == null) {
- mPendingCheckForLongPress = new CheckForLongPress();
- }
- mPendingCheckForLongPress.rememberWindowAttachCount();
- // 调用PostDelayed函数发送长按事件的异步延迟消息
- postDelayed(mPendingCheckForLongPress,
- ViewConfiguration.getLongPressTimeout() - delayOffset);
- }
3)CheckForLongPress类
该类定义了长按操作发生时的响应处理,同样实现了Runnable接口
- class CheckForLongPress implements Runnable {
- private int mOriginalWindowAttachCount;
- public void run() {
- // 进入该函数,说明检测到了长按操作
- if (isPressed() && (mParent != null)
- && mOriginalWindowAttachCount == mWindowAttachCount) {
- if (performLongClick()) {
- mHasPerformedLongPress = true;
- }
- }
- }
- public void rememberWindowAttachCount() {
- mOriginalWindowAttachCount = mWindowAttachCount;
- }
- }
- public boolean performLongClick() {
- sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_LONG_CLICKED);
- boolean handled = false;
- if (mOnLongClickListener != null) {
- // 回调用户实现的长按操作监听函数(OnLongClickListener)
- handled = mOnLongClickListener.onLongClick(View.this);
- }
- if (!handled) {
- // 如果OnLongClickListener的onLongClick返回false
- // 则需要继续处理该长按事件,这里是显示上下文菜单
- handled = showContextMenu();
- }
- if (handled) {
- // 长按操作事件被处理了,此时应该给用户触觉上的反馈
- performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
- }
- return handled;
- }
Android系统中长按事件的实现机制解析的更多相关文章
- [学习总结]4、Android的ViewGroup中事件的传递机制(一)
本文主要针对dispatchTouchEvent,onInterceptTouchEvent,onTouchEvent三个方法,通过简单的例子来简单的介绍下. 根据字面意思的理解,dispatchTo ...
- [学习总结]5、Android的ViewGroup中事件的传递机制(二)
下面是第一篇的连接 Android的ViewGroup中事件的传递机制(一) 关于onInterceptTouchEvent和onTouchEvent的详细解释. 1 public class Mai ...
- Android系统中自定义按键的短按、双击、长按事件
在项目中碰到这样的问题: 由于系统中的按键在底层做了重新定义或者新增了按键,此时需要在APP层对按键事件(keyevent)做分解处理,模拟Android系统做法,把keyevent分解成: 1.单击 ...
- Android系统中的广播(Broadcast)机制简要介绍和学习计划
在Android系统中,广播(Broadcast)是在组件之间传播数据(Intent)的一种机制:这些组件甚至是可以位于不同的进程中,这样它就像Binder机制一样,起到进程间通信的作用:本文通过一个 ...
- Android系统init进程启动及init.rc全解析
转:https://blog.csdn.net/zhonglunshun/article/details/78615980 服务启动机制system/core/init/init.c文件main函数中 ...
- Android系统匿名共享内存Ashmem(Anonymous Shared Memory)驱动程序源代码分析
文章转载至CSDN社区罗升阳的安卓之旅,原文地址:http://blog.csdn.net/luoshengyang/article/details/6664554 在上一文章Android系统匿名共 ...
- 【转】Android系统开篇
版权声明:本站所有博文内容均为原创,转载请务必注明作者与原文链接,且不得篡改原文内容.另外,未经授权文章不得用于任何商业目的. 一.引言 Android系统非常庞大.错综复杂,其底层是采用Linux作 ...
- Android Touch事件传递机制解析 (推荐)
最近新闻列表里的下拉 down up move 等等让我十分头疼 ,无意间看到了一篇非常不错的帖子,转载如下: 开篇语:最近程序在做一个小效果,要用到touch,结果整得云里面雾里的,干脆就好好把a ...
- Android Touch事件传递机制解析
android系统中的每个ViewGroup的子类都具有下面三个和TouchEvent处理密切相关的方法: 1)public boolean dispatchTouchEvent(MotionEven ...
随机推荐
- Linux wget下载https类型文件报错解决方法 转自老左博客
原文链接:http://www.laozuo.org/3648.html 一般我们远程调用下载文件直接用wget就可以,一般文件路径类型是http.如果有遇到是https就会下载出错,稍微不注意的新手 ...
- Python Socket Programming
本文介绍使用Python进行Socket网络编程,假设读者已经具备了基本的网络编程知识和Python的基本语法知识,本文中的代码如果没有说明则都是运行在Python 3.4下. Python的sock ...
- C语言刷新缓冲区(转载)
C语言中有几个基本输入函数: //获取字符系列 int fgetc(FILE *stream); int getc(FILE *stream); int getchar(void); //获取行系列 ...
- MySQL触发器之审计功能(转)
[导读] 最近ITPUB技术论坛特意组织网络性讨论活动,关于数据库审计的话题,分享各自公司如何实现数据库审计.个人经验和构想,以及数据库审计的技巧,刚好有网友发了一个典型的审计需求,要帮他分析,以及教 ...
- IE11新特性 -- Internet Explorer 11:请不要再叫我IE
Internet Explorer 11 中的一些新特性,包括对WebGL 的支持.预抓取.预渲染.flexbox.mutationobservers 以及其他一些 Web 标准的支持.但是更有趣的是 ...
- 为git配置ssh
git clone有两种方式,一种是http,一种是ssh. 配置ssh的好处是:在每次push代码的时候不需要输入密码. bash上生成秘钥: ssh-keygen -t rsa -C " ...
- sitecore(key\value\language)的灵活应用
1.当我们在做网站的时候是否会因为一个页面的文字变动来回改变.这样的麻烦sitecore都帮我们解决了. 2.sitecore分类key和value和语言几个维度.不同的key会因为不同的语言显示不同 ...
- 系统学下POWERSHELL吧,工作当中可能用得到呢。不能像以前那样修修改改了。
把环境,编辑器,版本这些都弄清楚,说不好还能把FCL类库弄懂个大概???:) [DateTime]::IsLeapYear(2008) $result = [DateTime]"06/21/ ...
- Largest product in a series
这个我开始理解错了,算错了. 我以为是求连续5个数的最大值,结果,是连接5个数相乘的最大值. 英语不好,容易吃亏啊. Find the greatest product of five consecu ...
- Linux内核定时器
Linux使用struct timer_list来描述一个定时器. 重要成员: expires:定时时长 *function:超时执行函数名使用流程: 1.定义定时器变量 /*定义定时器变量结构 ...