Android 触摸事件机制(三) View中触摸事件详解
本文将对View中触摸事件相关的内容进行介绍。重点介绍的是dispatchTouchEvent(), onTouchEvent()这两个API以及OnTouchListener接口。
注意:本文是基于Android 4.4.2版本进行介绍的!
目录
1. View中触摸事件的概述
2. View中触摸事件的源码解析
2.1. View中的dispatchTouchEvent
2.2. View中的onTouchEvent
1. View中触摸事件的概述
View中与触摸事件相关的内容可以分为两部分。
第一部分 dispatchTouchEvent()和onTouchEvent()这两个API
dispatchTouchEvent()是传递触摸事件的API,而onTouchEvent()则是View处理触摸事件的API。
View中dispatchTouchEvent()将事件传递给"自己的onTouch()", "自己的onTouchEvent()"进行处理。 onTouch()是OnTouchListener接口中API,属于View提供的,让用户自己处理触摸事件的接口。而onTouchEvent()是Android系统提供的,用于处理触摸事件的接口;在onTouchEvent()中会进行一系列的动作,例如获取焦点、设置按下状态,调用onClick()等。
第二部分 OnTouchListener, OnClickListener, OnLongClickListener等接口
这部分主要是接口。但本文主要介绍的是OnTouchListener接口中的onTouch()。为什么呢?
这是因为,onTouch()与onTouchEvent()都是用户处理触摸事件的API。
但不同的是:(01), onTouch()是View提供给用户,让用户自己处理触摸事件的接口。而onTouchEvent()是Android系统自己实现的接口。(02),onTouch()的优先级比onTouchEvent()的优先级更高。dispatchTouchEvent()中分发事件的时候,会先将事件分配给onTouch()进行处理,然后才分配给onTouchEvent()进行处理。 如果onTouch()对触摸事件进行了处理,并且返回true;那么,该触摸事件就不会分配在分配给onTouchEvent()进行处理了。只有当onTouch()没有处理,或者处理了但返回false时,才会分配给onTouchEvent()进行处理。
public interface OnTouchListener {boolean onTouch(View v, MotionEvent event);
}
2. View中触摸事件的源码解析
2.1 View中的dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent event) {if (mInputEventConsistencyVerifier != null) {mInputEventConsistencyVerifier.onTouchEvent(event, 0);}// 如果该View被遮蔽,并且该View在被遮蔽时不响应点击事件;// 此时,返回false;不会执行onTouch()或onTouchEvent(),即过滤调用该点击事件。// 否则,返回true。// 被遮蔽的意思是:该View不是位于顶部,有其他的View在它之上。if (onFilterTouchEventForSecurity(event)) {//noinspection SimplifiableIfStatementListenerInfo li = mListenerInfo;if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED&& li.mOnTouchListener.onTouch(this, event)) {return true;}if (onTouchEvent(event)) {return true;}}if (mInputEventConsistencyVerifier != null) {mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);}return false;
}
说明:该代码定义在frameworks/base/core/java/android/view/View.java中。
(01) mInputEventConsistencyVerifier是调试用的,这里不用理会。重点看onFilterTouchEventForSecurity()部分。
(02) onFilterTouchEventForSecurity()表示是否要分发该触摸事件;如果该View不是位于顶部,并且有设置属性使该View不在顶部时不响应触摸事件,则不分发该触摸事件,即不会执行onTouch()与onTouchEvent()。 否则的话,则将事件分发给onTouch(), onTouchEvent()进行处理。 (03) 如果将事件进行分发的话,会先尝试分发给onTouch();然后才分发给onTouchEvent()。
2.1.1 View中的onFilterTouchEventForSecurity()
public boolean onFilterTouchEventForSecurity(MotionEvent event) {//noinspection RedundantIfStatementif ((mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0&& (event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {// Window is obscured, drop this touch.return false;}return true;
}
说明:onFilterTouchEventForSecurity()返回true,表示可以分发该触摸事件;否则,不能分发该触摸事件。不能分发事件的情况,只有mViewFlags&FILTER_TOUCHES_WHEN_OBSCURED!=0,并且event.getFlags()&MotionEvent.FLAG_WINDOW_IS_OBSCURED!=0同时成立。
(01) FILTER_TOUCHES_WHEN_OBSCURED是android:filterTouchesWhenObscured属性所对应的位。android:filterTouchesWhenObscured是true的话,则表示其他视图在该视图之上,导致该视图被隐藏时,该视图就不再响应触摸事件。
(02) MotionEvent.FLAG_WINDOW_IS_OBSCURED为true的话,则表示该视图的窗口是被隐藏的。
2.2 View中的onTouchEvent
public boolean onTouchEvent(MotionEvent event) {final int viewFlags = mViewFlags;// 如果View被禁用的话,则返回它是否可以点击。if ((viewFlags & ENABLED_MASK) == DISABLED) {if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {setPressed(false);}return (((viewFlags & CLICKABLE) == CLICKABLE ||(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));}// 如果该View的mTouchDelegate不为null的话,将触摸消息分发给mTouchDelegate。// mTouchDelegate的默认值是null。if (mTouchDelegate != null) {if (mTouchDelegate.onTouchEvent(event)) {return true;}}// 如果View可以被点击的话,则执行if里面的内容。// 这其中涉及到的主要是获取焦点,设置按下状态,触发onClick(), onLongClick()事件等等。if (((viewFlags & CLICKABLE) == CLICKABLE ||(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {switch (event.getAction()) {case MotionEvent.ACTION_UP:boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {// take focus if we don't have it already and we should in// touch mode.boolean focusTaken = false;if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {focusTaken = requestFocus();}if (prepressed) {// The button is being released before we actually// showed it as pressed. Make it show the pressed// state now (before scheduling the click) to ensure// the user sees it.setPressed(true);}if (!mHasPerformedLongPress) {// This is a tap, so remove the longpress checkremoveLongPressCallback();// Only perform take click actions if we were in the pressed stateif (!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) {postDelayed(mUnsetPressedState,ViewConfiguration.getPressedStateDuration());} else if (!post(mUnsetPressedState)) {// If the post failed, unpress right nowmUnsetPressedState.run();}removeTapCallback();}break;case MotionEvent.ACTION_DOWN:mHasPerformedLongPress = false;if (performButtonActionOnTouchDown(event)) {break;}// Walk up the hierarchy to determine if we're inside a scrolling container.boolean isInScrollingContainer = isInScrollingContainer();// For views inside a scrolling container, delay the pressed feedback for// a short period in case this is a scroll.if (isInScrollingContainer) {mPrivateFlags |= PFLAG_PREPRESSED;if (mPendingCheckForTap == null) {mPendingCheckForTap = new CheckForTap();}postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());} else {// Not inside a scrolling container, so show the feedback right awaysetPressed(true);checkForLongClick(0);}break;case MotionEvent.ACTION_CANCEL:setPressed(false);removeTapCallback();removeLongPressCallback();break;case MotionEvent.ACTION_MOVE:final int x = (int) event.getX();final int y = (int) event.getY();// Be lenient about moving outside of buttonsif (!pointInView(x, y, mTouchSlop)) {// Outside buttonremoveTapCallback();if ((mPrivateFlags & PFLAG_PRESSED) != 0) {// Remove any future long press/tap checksremoveLongPressCallback();setPressed(false);}}break;}return true;}return false;
}
说明:onTouchEvent()是Android系统实现的View对触摸事件的处理。
(01) 如果View被禁用的话,则返回它是否可以点击。当我们调用了setEnabled(false)时,View就被禁用了;默认情况下,View是可用的。当调用setClickable(true)或者android:clickable为true时,View就是可点击状态;默认情况下,View是不可点击的。
(02) 如果该View的mTouchDelegate不为null的话,将触摸消息分发给mTouchDelegate。例如,假设有两个视图v1和v2,它们的布局相互之间不重叠;如果设置了v1.setTouchDelegate(v2)的话,v1的触摸事件就会分发给v2。 注意:mTouchDelegate的默认值是null。
(03) 如果View可以被点击的话,则执行if里面的内容。if里面涉及的内容很多,这里与本文的主题关联不大,暂且不表;如果要细将的话,估计得好几篇文章。例如,setPressed()是设置View的按下状态,如果用户有设置View在不同状态的图片时,setPressed()时会导致View的图片的更新。
View中关于触摸事件的代码就分析至此。总的来说:
(01) View中的dispatchTouchEvent()会将事件传递给"自己的onTouch()", "自己的onTouchEvent()"进行处理。而且onTouch()的优先级比onTouchEvent()的优先级要高。 (02) onTouch()与onTouchEvent()都是View中用户处理触摸事件的API。onTouch是OnTouchListener接口中的函数,OnTouchListener接口需要用户自己实现。onTouchEvent()是View自带的接口,Android系统提供了默认的实现;当然,用户可以重载该API。
(03) onTouch()与onTouchEvent()有两个不同之处:(01), onTouch()是View提供给用户,让用户自己处理触摸事件的接口。而onTouchEvent()是Android系统自己实现的接口。(02),onTouch()的优先级比onTouchEvent()的优先级更高。dispatchTouchEvent()中分发事件的时候,会先将事件分配给onTouch()进行处理,然后才分配给onTouchEvent()进行处理。 如果onTouch()对触摸事件进行了处理,并且返回true;那么,该触摸事件就不会分配在分配给onTouchEvent()进行处理了。只有当onTouch()没有处理,或者处理了但返回false时,才会分配给onTouchEvent()进行处理。
Android 触摸事件机制(三) View中触摸事件详解相关推荐
- html事件机制,浅析JavaScript中的事件机制_基础知识
事件是什么 ? JavaScript与HTML交互是通过在用户或浏览器操纵页面上发生的事件进行处理. 当页面加载,这是一个事件.当用户点击一个按钮,这一下,也就是一个事件.事件的另一个例子是类似按下任 ...
- 【Android游戏开发之八】游戏中添加音频-详解MediaPlayer与SoundPoo!并讲解两者的区别和游戏中的用途!...
为什么80%的码农都做不了架构师?>>> 李华明Himi 原创,转载务必在明显处注明: 转载自 [黑米GameDev街区] 原文链接: http://www.himigam ...
- 【Android游戏开发之八】游戏中添加音频-详解MediaPlayer与SoundPool的利弊以及各个在游戏中的用途!...
本站文章均为 李华明Himi 原创,转载务必在明显处注明: 转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/android-game/312.html 游 ...
- 【Android 事件分发】事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 )
Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) 文章目录 ...
- View绘制体系(三)——AttributeSet与TypedArray详解
View绘制体系(三)--AttributeSet与TypedArray详解 前言 上篇博客中讲了LayoutInflater.inflate机制,其中提到了AttributeSet和XmlPullP ...
- Android复习14【高级编程:推荐网址、抠图片上的某一角下来、Bitmap引起的OOM问题、三个绘图工具类详解、画线条、Canvas API详解(平移、旋转、缩放、倾斜)、矩阵详解】
目 录 推荐网址 抠图片上的某一角下来 8.2.2 Bitmap引起的OOM问题 8.3.1 三个绘图工具类详解 画线条 8.3.16 Canvas API详解(Part 1) 1.transla ...
- Android基础入门教程——8.3.1 三个绘图工具类详解
Android基础入门教程--8.3.1 三个绘图工具类详解 标签(空格分隔): Android基础入门教程 本节引言: 上两小节我们学习了Drawable以及Bitmap,都是加载好图片的,而本节我 ...
- Android中mesure过程详解 (结合Android 4.0.4 最新源码)
如何遍历并绘制View树?之前的文章Android中invalidate() 函数详解(结合Android 4.0.4 最新源码)中提到invalidate()最后会发起一个View树遍历的请求,并通 ...
- android调webview的方法,Android中的WebView详解
Android中的WebView详解 WebView详解 基本用法 布局文件配置WebView android:id="@+id/wv_news_detail" android:l ...
最新文章
- linux显示不在sudoers文件中,Ubuntu无法使用sudo提权提示当前用户不在sudoers文件中...
- x264编码参数大测试:10 trellis(1000Kbps)
- YJX_rxjh_10_2.5.2
- 进入Linux救援(rescue)模式的四大法门
- 友邦人寿引入阿里云PolarDB云数据库 支撑保险业务系统加速上云
- ctf.360.cn第二届,逆向部分writeup——第三题
- python调用c#注意事项_Python调用C#编写的DLL
- log4js linux,日志管理 log4js
- 技嘉z77主板msata速度_技嘉小雕、微星迫击炮、华硕电竞特工三款主板对比
- 基于 snowNLP的微博评论数据情感分析
- 32位与64位架构上的区别
- 闪存flash基础原理
- adobe premiere 不支持的视频驱动程序
- 【计算机网络】根据IP地址计算网络地址
- 为什么你招不到合适的猎头顾问?浅谈猎企内部招聘
- vue Emitter
- 超市进销存之openGauss数据库的应用与实践
- 2022G3锅炉水处理国家题库及答案
- 用Python基本语法实现一个购物车功能
- 阿里云ECS服务器部署