本文将对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中触摸事件详解相关推荐

  1. html事件机制,浅析JavaScript中的事件机制_基础知识

    事件是什么 ? JavaScript与HTML交互是通过在用户或浏览器操纵页面上发生的事件进行处理. 当页面加载,这是一个事件.当用户点击一个按钮,这一下,也就是一个事件.事件的另一个例子是类似按下任 ...

  2. 【Android游戏开发之八】游戏中添加音频-详解MediaPlayer与SoundPoo!并讲解两者的区别和游戏中的用途!...

    为什么80%的码农都做不了架构师?>>>     李华明Himi 原创,转载务必在明显处注明: 转载自 [黑米GameDev街区] 原文链接:  http://www.himigam ...

  3. 【Android游戏开发之八】游戏中添加音频-详解MediaPlayer与SoundPool的利弊以及各个在游戏中的用途!...

    本站文章均为 李华明Himi 原创,转载务必在明显处注明: 转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/android-game/312.html 游 ...

  4. 【Android 事件分发】事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 )

    Android 事件分发 系列文章目录 [Android 事件分发]事件分发源码分析 ( 驱动层通过中断传递事件 | WindowManagerService 向 View 层传递事件 ) 文章目录 ...

  5. View绘制体系(三)——AttributeSet与TypedArray详解

    View绘制体系(三)--AttributeSet与TypedArray详解 前言 上篇博客中讲了LayoutInflater.inflate机制,其中提到了AttributeSet和XmlPullP ...

  6. Android复习14【高级编程:推荐网址、抠图片上的某一角下来、Bitmap引起的OOM问题、三个绘图工具类详解、画线条、Canvas API详解(平移、旋转、缩放、倾斜)、矩阵详解】

    目   录 推荐网址 抠图片上的某一角下来 8.2.2 Bitmap引起的OOM问题 8.3.1 三个绘图工具类详解 画线条 8.3.16 Canvas API详解(Part 1) 1.transla ...

  7. Android基础入门教程——8.3.1 三个绘图工具类详解

    Android基础入门教程--8.3.1 三个绘图工具类详解 标签(空格分隔): Android基础入门教程 本节引言: 上两小节我们学习了Drawable以及Bitmap,都是加载好图片的,而本节我 ...

  8. Android中mesure过程详解 (结合Android 4.0.4 最新源码)

    如何遍历并绘制View树?之前的文章Android中invalidate() 函数详解(结合Android 4.0.4 最新源码)中提到invalidate()最后会发起一个View树遍历的请求,并通 ...

  9. android调webview的方法,Android中的WebView详解

    Android中的WebView详解 WebView详解 基本用法 布局文件配置WebView android:id="@+id/wv_news_detail" android:l ...

最新文章

  1. linux显示不在sudoers文件中,Ubuntu无法使用sudo提权提示当前用户不在sudoers文件中...
  2. x264编码参数大测试:10 trellis(1000Kbps)
  3. YJX_rxjh_10_2.5.2
  4. 进入Linux救援(rescue)模式的四大法门
  5. 友邦人寿引入阿里云PolarDB云数据库 支撑保险业务系统加速上云
  6. ctf.360.cn第二届,逆向部分writeup——第三题
  7. python调用c#注意事项_Python调用C#编写的DLL
  8. log4js linux,日志管理 log4js
  9. 技嘉z77主板msata速度_技嘉小雕、微星迫击炮、华硕电竞特工三款主板对比
  10. 基于 snowNLP的微博评论数据情感分析
  11. 32位与64位架构上的区别
  12. 闪存flash基础原理
  13. adobe premiere 不支持的视频驱动程序
  14. 【计算机网络】根据IP地址计算网络地址
  15. 为什么你招不到合适的猎头顾问?浅谈猎企内部招聘
  16. vue Emitter
  17. 超市进销存之openGauss数据库的应用与实践
  18. 2022G3锅炉水处理国家题库及答案
  19. 用Python基本语法实现一个购物车功能
  20. 阿里云ECS服务器部署

热门文章

  1. 网易云音乐移动端项目实战(分解下)
  2. 5.分别画出下列二次曲面
  3. 直方图均衡化背后的数学
  4. vue中click无效问题
  5. Anaconda Navigator启动缓慢
  6. js 屏蔽原生鼠标和键盘事件
  7. 图片数据增强的方法——收藏
  8. w10用计算机卸载,win10电脑怎么卸载软件
  9. 虚拟机共享目录添加按钮灰色_共享按钮将成为PS4最重要的遗产
  10. HTML5的最简单模板