Touch事件分发源码解析
1、先看ViewGroup的dispatchOnTouchEvent(MotionEvent e)的源码
1.1 主要是获取一些坐标值,留备后用
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (!onFilterTouchEventForSecurity(ev)) {
return false;
} final int action = ev.getAction();
final float xf = ev.getX();
final float yf = ev.getY();
final float scrolledXFloat = xf + mScrollX;
final float scrolledYFloat = yf + mScrollY;
final Rect frame = mTempRect;
1.2 先处理DOWN事件
//TODO 1、判断是不是子View不允许老子拦截Touch事件
boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0; if (action == MotionEvent.ACTION_DOWN) {
if (mMotionTarget != null) {
// this is weird, we got a pen down, but we thought it was
// already down!
// XXX: We should probably send an ACTION_UP to the current
// target.
mMotionTarget = null;
}
// If we're disallowing intercept or if we're allowing and we didn't
// intercept
//TODO 2、如果子View不允许,或者老子自己不想拦截,则进入这里
if (disallowIntercept || !onInterceptTouchEvent(ev)) {
// reset this event's action (just to protect ourselves)
ev.setAction(MotionEvent.ACTION_DOWN);
// We know we want to dispatch the event down, find a child
// who can handle it, start with the front-most child.
final int scrolledXInt = (int) scrolledXFloat;
final int scrolledYInt = (int) scrolledYFloat;
final View[] children = mChildren;
final int count = mChildrenCount;
//TODO 3、根据用户落指的坐标,找到应该响应该Touch事件的子View或者子ViewGroup
for (int i = count - 1; i >= 0; i--) {
final View child = children[i];
if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
|| child.getAnimation() != null) {
child.getHitRect(frame);
//TODO 4、很明显,根据范围来判断的
if (frame.contains(scrolledXInt, scrolledYInt)) {
// offset the event to the view's coordinate system
final float xc = scrolledXFloat - child.mLeft;
final float yc = scrolledYFloat - child.mTop;
ev.setLocation(xc, yc);
child.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
//TODO 5、找到子View,将事件交给它,如果它愿意消费DOWN事件,则记录下来这个龟儿子,并返回
if (child.dispatchTouchEvent(ev)) {
// Event handled, we have a target now.
mMotionTarget = child;
return true;
}
// The event didn't get handled, try the next view.
// Don't reset the event's location, it's not
// necessary here.
}
}
}
}
}
看见上面5条注释了吧,够用了
1.3 没找到子View、子View不愿消费(注释5)、或者不是DOWN事件,如下
boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
(action == MotionEvent.ACTION_CANCEL); if (isUpOrCancel) {
// Note, we've already copied the previous state to our local
// variable, so this takes effect on the next event
mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
} // The event wasn't an ACTION_DOWN, dispatch it to our target if
// we have one.
12 // TODO 1、如果是没有子View,不管是DOWN还是其他事件,都交给自己来处理,包括后续的MOVE、UP事件。
13 // 因为这个方法有点类似递归调用,所以如果自己也不想处理,那么父类会分发给自己
final View target = mMotionTarget;
if (target == null) {
// We don't have a target, this means we're handling the
// event as a regular view.
ev.setLocation(xf, yf);
if ((mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
ev.setAction(MotionEvent.ACTION_CANCEL);
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
}
// TODO 2、调用View的dispatchTouchEvent,会调用到自己的onTouchEvent()方法
return super.dispatchTouchEvent(ev);
}
这段代码就一个意思,不管啥事件,没有子View,老子亲自处理~
1.4 找到子View,继续往下走
1 // TODO 1、找到子View,子View让老子拦截,或者老子自己想拦截,给龟孙一个CANCEL,
2 // 并告诉父ViewGroup,老子要了,而且将mMotionTarget置为null,这样后续事件直接走上面的判断,直接找老子处理
// if have a target, see if we're allowed to and want to intercept its
// events
if (!disallowIntercept && onInterceptTouchEvent(ev)) {
final float xc = scrolledXFloat - (float) target.mLeft;
final float yc = scrolledYFloat - (float) target.mTop;
mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
ev.setAction(MotionEvent.ACTION_CANCEL);
ev.setLocation(xc, yc);
if (!target.dispatchTouchEvent(ev)) {
// target didn't handle ACTION_CANCEL. not much we can do
// but they should have.
}
// clear the target
mMotionTarget = null;
// Don't dispatch this event to our own view, because we already
// saw it when intercepting; we just want to give the following
// event to the normal onTouchEvent().
return true;
} // TODO 2、如果是UP、CANCEL事件,则清空
if (isUpOrCancel) {
mMotionTarget = null;
}
这段代码,龟孙之前说的好好的, 要自己消费事件,突然又不想了。或者老子突然想拦截了,那么老子自己拦截,自己处理
1.5 没有幺蛾子了,交给龟孙自己处理
// finally offset the event to the target's coordinate system and
// dispatch the event.
final float xc = scrolledXFloat - (float) target.mLeft;
final float yc = scrolledYFloat - (float) target.mTop;
ev.setLocation(xc, yc); if ((target.mPrivateFlags & CANCEL_NEXT_UP_EVENT) != 0) {
ev.setAction(MotionEvent.ACTION_CANCEL);
target.mPrivateFlags &= ~CANCEL_NEXT_UP_EVENT;
mMotionTarget = null;
} // TODO 1、不管前面这几行了,交给龟孙去处理
return target.dispatchTouchEvent(ev);
ViewGroup没有重写View的onTouchEvent()方法,onInterceptTouchEvent()返回false,默认不拦截
2、 View的相关方法
2.1 dispatchTouchEvent()方法
就一点点,调用自己的onTouchEvent()方法,并以onTouchEvent的返回值为返回值
/**
* Pass the touch screen motion event down to the target view, or this
* view if it is the target.
*
* @param event The motion event to be dispatched.
* @return True if the event was handled by the view, false otherwise.
*/
public boolean dispatchTouchEvent(MotionEvent event) {
if (!onFilterTouchEventForSecurity(event)) {
return false;
} if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
2.2 onTouchEvent()方法
这个方法比较复杂,不可点击的时候返回false,说明自己不处理。可点击的时候,根据不同的事件类型进行处理
public boolean onTouchEvent(MotionEvent event) {
final int viewFlags = mViewFlags; if ((viewFlags & ENABLED_MASK) == DISABLED) {
// A disabled view that is clickable still consumes the touch
// events, it just doesn't respond to them.
return (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
} if (mTouchDelegate != null) {
if (mTouchDelegate.onTouchEvent(event)) {
return true;
}
} if (((viewFlags & CLICKABLE) == CLICKABLE ||
(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
boolean prepressed = (mPrivateFlags & PREPRESSED) != 0;
if ((mPrivateFlags & 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 (!mHasPerformedLongPress) {
// This is a tap, so remove the longpress check
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();
}
break; case MotionEvent.ACTION_DOWN:
if (mPendingCheckForTap == null) {
mPendingCheckForTap = new CheckForTap();
}
mPrivateFlags |= PREPRESSED;
mHasPerformedLongPress = false;
postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
break; case MotionEvent.ACTION_CANCEL:
mPrivateFlags &= ~PRESSED;
refreshDrawableState();
removeTapCallback();
break; case MotionEvent.ACTION_MOVE:
final int x = (int) event.getX();
final int y = (int) event.getY(); // Be lenient about moving outside of buttons
int slop = mTouchSlop;
if ((x < 0 - slop) || (x >= getWidth() + slop) ||
(y < 0 - slop) || (y >= getHeight() + slop)) {
// Outside button
removeTapCallback();
if ((mPrivateFlags & PRESSED) != 0) {
// Remove any future long press/tap checks
removeLongPressCallback(); // Need to switch from pressed to not pressed
mPrivateFlags &= ~PRESSED;
refreshDrawableState();
}
}
break;
}
return true;
} return false;
}
Touch事件分发源码解析的更多相关文章
- ViewGroup事件分发源码分析
1.AndroidStudio源码调试方式 AndroidStudio默认是支持一部分源码调试的,但是build.gradle(app) 中的sdk版本要保持一致, 最好是编译版本.运行版本以及手机的 ...
- Qt中事件分发源码剖析
Qt中事件分发源码剖析 Qt中事件传递顺序: 在一个应该程序中,会进入一个事件循环,接受系统产生的事件,而且进行分发,这些都是在exec中进行的. 以下举例说明: 1)首先看看以下一段演示样例代码: ...
- Android View事件分发源码分析
引言 上一篇文章我们介绍了View的事件分发机制,今天我们从源码的角度来学习下事件分发机制. Activity对点击事件的分发过程 事件最先传递给当前Activity,由Activity的dispat ...
- android事件分发源码分析—笔记
昨天晚上从源码角度复习了一下android的事件分发机制,今天将笔记整理下放在网上.其实说复习,也是按着<android开发艺术探索>这本书作者的思路做的笔记. 目录 事件是如何从Acti ...
- view事件分发源码理解
有些困难无法逃避,没办法,那就只有去解决它.view事件分发对我而言是一块很难啃的骨头,看了<安卓开发艺术探索>关于这个知识点的讲解,看了好几遍,始终不懂,最终通过调试分析结果,看博客,再 ...
- Android Touch事件传递机制全面解析(从WMS到View树)
转眼间近一年没更新博客了,工作一忙起来.非常难有时间来写博客了,因为如今也在从事Android开发相关的工作,因此以后的博文也会很多其它地专注于这一块. 这篇文章准备从源代码层面为大家带来Touch事 ...
- Android Touch事件相关源码【Android SourceCode 2.3.6】
2018-05-31 17:23:46 Note: 这里的源码来自Android 2.3.6,这个版本的代码比较简单,适合理解Touch事件的传递原理.后续版本源码复杂了很多,但是原理都是类似的. 2 ...
- 深入理解Spring系列之十:DispatcherServlet请求分发源码分析
转载 https://mp.weixin.qq.com/s/-kEjAeQFBYIGb0zRpST4UQ DispatcherServlet是SpringMVC的核心分发器,它实现了请求分发,是处理请 ...
- Abp领域事件(EventBus)源码解析
Abp中使用EventBus来解耦领域中的业务逻辑,也是订阅-发布模式的一种实现.简单来说就是,当我触发一个事件,注册了这个事件的处理器就会被找到并执行. 先看看整体代码结构 其中Entities文件 ...
随机推荐
- SSM-网站前台博客系统制作(1)---前台+Google的Kaptcha
前提: 1天半时间简单自己手写了一下前端布局和后台验证码的基本工作,简要说明一下遇到的问题和收获吧. 这次基本就是前台设计(首页)+Kaptcha图片验证码(之前弄了一个reCaptcha验证码 但是 ...
- SAP abap 内表增加字段方法,结构复用
1.include data: begin of gth_qamr. include structure qamr. data: kurztext like qamv-kurztext ...
- 单元测试系列之十:Sonar 常用代码规则整理(二)
摘要:帮助公司部署了一套sonar平台,经过一段时间运行,发现有一些问题出现频率很高,因此有必要将这些问题进行整理总结和分析,避免再次出现类似问题. 作者原创技术文章,转载请注明出处 ======== ...
- Summary on deep learning framework --- Torch7
Summary on deep learning framework --- Torch7 2018-07-22 21:30:28 1. 尝试第一个 CNN 的 torch版本, 代码如下: -- ...
- Javascript 中的数据类型判断
(迁移自旧博客2017 09 25) typeof 我们常使用typeof来判断数据类型,在常规场景中足以应付数据类型判断的需要: var obj = { name: 'zhangxiang' }; ...
- Springboot中Jackson的操作
有一段时间没写博客了,虽然是菜鸟一枚但毕竟总要有东西记录学习的,我相信有志者事竟成.今天在工作中使用Jackson转换了一个javabean,传到测试服上之后发现日期少了一天,使用的是@JsonFor ...
- Hadoop之搭建完全分布式运行模式
一.过程分析 1.准备3台客户机(关闭防火墙.修改静态ip.主机名称) 2.安装JDK 3.配置环境变量 4.安装Hadoop 5.配置集群 6.单点启动 7.配置ssh免密登录 8.群起并测试集群 ...
- 《图解Java多线程设计模式》读书笔记
略读中...后面详读的时候,补充经典图片和文字说明
- DAY 30 网络编程基础
一.软件开发架构 1.c/s架构 c:客户端 s:服务端 2.b/s架构 b:浏览器 c:服务器 手机端:好像C/S架构比较火,其实不然,微信小程序.支付宝第三方接口 B/S架构的优点是统一接口 PC ...
- LINUX内核PCI扫描过程
LINUX内核PCI扫描过程 内核版本 3.10.103 1. ACPI热插拔扫描subsys_initcall(acpi_init)@drivers/acpi/bus.c |-acpi_scan_i ...