android 事件分发机制
1.View的事件分发机制
一个button,简单一点就是onTouch,还有onclick事件,我们一个一个来分析
首先响应的是dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent event) {
if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
mOnTouchListener.onTouch(this, event)) {
return true;
}
return onTouchEvent(event);
}
其实,在android源码的命名还是很有规律的,dispatchXXX,也就是分发机制,往往就是第一个需要响应的地方。
我们来分析下:touchlistener不为空,也就是view的使用者设置了回调。
第二个条件就是View必须是enable的。第三:onTouch返回false,就说明onTouch不消费该事件,由OnTouchEvent响应。
如果返回True,那么就会直接return。
所以onClick事件一定会被调到。
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;
}
onTouchEvent
最终会走到performClick这个方法。
public boolean performClick() {
final boolean result;
final ListenerInfo li = mListenerInfo;
if (li != null && li.mOnClickListener != null) {
playSoundEffect(SoundEffectConstants.CLICK);
li.mOnClickListener.onClick(this);
result = true;
} else {
result = false;
}
sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
return result;
}
可以看到,如果setOnClickListener, onClick 就会走到。
2.ViewGroup的事件分发机制
<com.joyfulmath.frameworksample.viewdemo.MyLayout
android:id="@+id/my_layout"
android:background="#99000044"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/button_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="button"/>
<Button
android:id="@+id/imageId"
android:layout_centerInParent="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@android:drawable/ic_lock_power_off"/>
</com.joyfulmath.frameworksample.viewdemo.MyLayout>
一个layout里面有2个button,
package com.joyfulmath.frameworksample.viewdemo; import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.ImageView; import com.joyfulmath.frameworksample.R; /**
* Created by Administrator on 2016/8/27 0027.
*/
public class TestViewAction extends Activity implements View.OnClickListener,View.OnTouchListener
{
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.content_main);
Button button = (Button) findViewById(R.id.button_id);
button.setOnClickListener(this);
button.setOnTouchListener(this);
Button imageView = (Button) findViewById(R.id.imageId);
imageView.setOnClickListener(this);
imageView.setOnTouchListener(this);
MyLayout myLayout = (MyLayout) findViewById(R.id.my_layout);
myLayout.setOnTouchListener(this);
myLayout.setOnClickListener(this);
} @Override
public void onClick(View v) {
switch (v.getId())
{
case R.id.button_id:
TraceLog.i("button_id");
break;
case R.id.imageId:
TraceLog.i("imageId");
break;
case R.id.my_layout:
TraceLog.i("my_layout");
break;
} } @Override
public boolean onTouch(View v, MotionEvent event) {
switch (v.getId())
{
case R.id.button_id:
TraceLog.i("button_id");
break;
case R.id.imageId:
TraceLog.i("imageId");
break;
case R.id.my_layout:
TraceLog.i("my_layout");
break;
}
return false;
}
}
TestViewAction

分别点击button1 & button2 & 灰色部分
等到log如下:
08-27 10:19:26.799 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: button_id [at (TestViewAction.java:55)]
08-27 10:19:26.880 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: button_id [at (TestViewAction.java:55)]
08-27 10:19:26.896 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: button_id [at (TestViewAction.java:55)]
08-27 10:19:26.913 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: button_id [at (TestViewAction.java:55)]
08-27 10:19:26.926 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: button_id [at (TestViewAction.java:55)]
08-27 10:19:26.926 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onClick: button_id [at (TestViewAction.java:38)]
08-27 10:19:27.434 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: imageId [at (TestViewAction.java:58)]
08-27 10:19:27.535 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: imageId [at (TestViewAction.java:58)]
08-27 10:19:27.543 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: imageId [at (TestViewAction.java:58)]
08-27 10:19:27.544 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onClick: imageId [at (TestViewAction.java:41)]
08-27 10:19:28.111 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: my_layout [at (TestViewAction.java:61)]
08-27 10:19:28.156 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: my_layout [at (TestViewAction.java:61)]
08-27 10:19:28.173 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: my_layout [at (TestViewAction.java:61)]
08-27 10:19:28.190 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: my_layout [at (TestViewAction.java:61)]
08-27 10:19:28.237 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onTouch: my_layout [at (TestViewAction.java:61)]
08-27 10:19:28.237 2120-2120/com.joyfulmath.frameworksample I/TestViewAction: onClick: my_layout [at (TestViewAction.java:44)]
也就是点击button1以后,不会传递都layout
But,如果layout里面有一个函数
public boolean onInterceptTouchEvent(MotionEvent ev)
这个函数就是截断对button的分发处理,默认是return false。
至此,我们有了一个大概的流程。
Activtiy->ViewGroup->View
如果仔细分析就会发现,在Activity里面有一个getDocView。所以Activity里面有个RootView的概念。
言归正传,ViewGroup本质上也是一个View,所以,可以把模型简单的定性为Activtiy->ViewGroup->View 三层。
首先Activity里面有2个函数,我们分析看看:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
TraceLog.i();
return super.dispatchTouchEvent(ev);
} @Override
public boolean onTouchEvent(MotionEvent event) {
TraceLog.i();
return super.onTouchEvent(event);
}
所以大体流程如下:
1.@Activty.diapatchTouchEvent ->@Layout.dispatchTouchEvent->@layout.onInterceptTouchEvent return true/false
2.return true->@layout.onTouchEvent 后面部分同view
3.return false->@view.dispatchTouchEvent View的分发见上一片流程。
上面是2016年的文章分析,最新的java层的分析可以参考如下文章
关注公共号:

参考:
《深入理解android设计思想》 林学森
android 事件分发机制的更多相关文章
- Android事件分发机制(下)
这篇文章继续讨论Android事件分发机制,首先我们来探讨一下,什么是ViewGroup?它和普通的View有什么区别? 顾名思义,ViewGroup就是一组View的集合,它包含很多的子View和子 ...
- Android事件分发机制(上)
Android事件分发机制这个问题不止一个人问过我,每次我的回答都显得模拟两可,是因为自己一直对这个没有很好的理解,趁现在比较闲对这个做一点总结 举个例子: 你当前有一个非常简单的项目,只有一个Act ...
- android事件分发机制
android事件分发机制,给控件设置ontouch监听事件,当ontouch返回true时,他就不会走onTouchEvent方法,要想走onTouchEvent方法只需要返回ontouch返回fa ...
- [转]Android事件分发机制完全解析,带你从源码的角度彻底理解(上)
Android事件分发机制 该篇文章出处:http://blog.csdn.net/guolin_blog/article/details/9097463 其实我一直准备写一篇关于Android事件分 ...
- Android事件分发机制源码分析
Android事件分发机制源码分析 Android事件分发机制源码分析 Part1事件来源以及传递顺序 Activity分发事件源码 PhoneWindow分发事件源码 小结 Part2ViewGro ...
- 【转】Android事件分发机制完全解析,带你从源码的角度彻底理解(下)
转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9153761 记得在前面的文章中,我带大家一起从源码的角度分析了Android中Vi ...
- 【自己定义控件】android事件分发机制
自己定义Viewgrou中我们或许会常常碰到这种情况,2个子控件的事件冲突导致滑动没实用了.滑动反应非常慢,点击没用了,要划非常多次才移动一点点等等.或许我们第一反应就是百度,google去搜索下答案 ...
- Android 事件分发机制具体解释
很多其它内容请參照我的个人网站: http://stackvoid.com/ 网上非常多关于Android事件分发机制的解释,大多数描写叙述的都不够清晰,没有吧来龙去脉搞清晰,本文将带你从Touch事 ...
- 【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发逻辑及经常遇到的一些“诡异”现象
前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/11039252.html]谢谢! 在上一篇文章[[朝花夕拾]Android自定义View篇之(五 ...
- 【朝花夕拾】Android自定义View篇之(五)Android事件分发机制(上)Touch三个重要方法的处理逻辑
前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/10998855.html]谢谢! 在自定义View中,经常需要处理Android事件分发的问题, ...
随机推荐
- geotrellis使用(六)Scala并发(并行)编程
本文主要讲解Scala的并发(并行)编程,那么为什么题目概称geotrellis使用(六)呢,主要因为本系列讲解如何使用Geotrellis,具体前几篇博文已经介绍过了.我觉得干任何一件事情基础很重要 ...
- C# 中使用Word文档对图像进行操作
C# 中使用Word文档对图像进行操作 Download Files: ImageOperationsInWord.zip 简介 在这篇文章中我们可以学到在C#程序中使用一个Word文档对图像的各种操 ...
- 使用openfiler设置SMB/CIFS共享总是不通过的一例与解决办法
最近使用openfiler进行空闲存储的集中化管理与多主机节点共享,等设置到了SMB/CIFS的时候总是通过不了,前提需要开启的LDAP内建在openfiler也都开启并设置好了,但就是无法通过&qu ...
- 让你的JS更优雅的小技巧
首先,看一个非常不优雅的例子: 看到这段代码,虽然代码很短,但是一眼看上去就不想再看了,也就是没什么可读性.这段代码,没有封装,随意定义一个变量都是全局变量,这样在多人开发或者是大型开发中,极其容易造 ...
- JS实现返回对象的详细信息
使用JS有时会需要打印出对象的详细信息,下面方法可以实现: function ShowObjProperty(Obj) { var PropertyList=''; var PropertyCount ...
- 创建ASP.NET Core MVC应用程序(6)-添加验证
创建ASP.NET Core MVC应用程序(6)-添加验证 DRY原则 DRY("Don't Repeat Yourself")是MVC的设计原则之一.ASP.NET MVC鼓励 ...
- MacOS使用AMPPS环境
下载(http://www.ampps.com/download)并安装AMPPS 基本配置:2.1选中所有扩展2.2 变更PHP版本为5.3 配置虚拟主机(Virtual Hosts) AMPP ...
- MySQL数据迁移到MSSQL-以小米数据库为例-测试828W最快可达到2分11秒
这里采用.NET Framework 4.0以上版本中新出现的 ConcurrentQueue<T> 类 MSDN是这样描述的: ConcurrentQueue<T> 类是一个 ...
- CodeFirst时使用T4模板(你肯定没用过的笨方法,还望园友指教)
我们都知道T4模板用于生成相似代码. 在DBFirst和ModelFirst条件下我们很容易从.edmx下获取所有实体类和其名称,并且通过我们定义的模板和某些遍历工作为我们生成所需要的相似代码. 但是 ...
- WPF CheckBox 自定义样式
WPF 自定义样式.CheckBox <Style x:Key="EmptyCheckBox" TargetType="CheckBox"> < ...