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层的分析可以参考如下文章

demanmath.com

关注公共号:

参考:

《深入理解android设计思想》   林学森

android 事件分发机制的更多相关文章

  1. Android事件分发机制(下)

    这篇文章继续讨论Android事件分发机制,首先我们来探讨一下,什么是ViewGroup?它和普通的View有什么区别? 顾名思义,ViewGroup就是一组View的集合,它包含很多的子View和子 ...

  2. Android事件分发机制(上)

    Android事件分发机制这个问题不止一个人问过我,每次我的回答都显得模拟两可,是因为自己一直对这个没有很好的理解,趁现在比较闲对这个做一点总结 举个例子: 你当前有一个非常简单的项目,只有一个Act ...

  3. android事件分发机制

    android事件分发机制,给控件设置ontouch监听事件,当ontouch返回true时,他就不会走onTouchEvent方法,要想走onTouchEvent方法只需要返回ontouch返回fa ...

  4. [转]Android事件分发机制完全解析,带你从源码的角度彻底理解(上)

    Android事件分发机制 该篇文章出处:http://blog.csdn.net/guolin_blog/article/details/9097463 其实我一直准备写一篇关于Android事件分 ...

  5. Android事件分发机制源码分析

    Android事件分发机制源码分析 Android事件分发机制源码分析 Part1事件来源以及传递顺序 Activity分发事件源码 PhoneWindow分发事件源码 小结 Part2ViewGro ...

  6. 【转】Android事件分发机制完全解析,带你从源码的角度彻底理解(下)

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9153761 记得在前面的文章中,我带大家一起从源码的角度分析了Android中Vi ...

  7. 【自己定义控件】android事件分发机制

    自己定义Viewgrou中我们或许会常常碰到这种情况,2个子控件的事件冲突导致滑动没实用了.滑动反应非常慢,点击没用了,要划非常多次才移动一点点等等.或许我们第一反应就是百度,google去搜索下答案 ...

  8. Android 事件分发机制具体解释

    很多其它内容请參照我的个人网站: http://stackvoid.com/ 网上非常多关于Android事件分发机制的解释,大多数描写叙述的都不够清晰,没有吧来龙去脉搞清晰,本文将带你从Touch事 ...

  9. 【朝花夕拾】Android自定义View篇之(六)Android事件分发机制(中)从源码分析事件分发逻辑及经常遇到的一些“诡异”现象

    前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/11039252.html]谢谢! 在上一篇文章[[朝花夕拾]Android自定义View篇之(五 ...

  10. 【朝花夕拾】Android自定义View篇之(五)Android事件分发机制(上)Touch三个重要方法的处理逻辑

    前言 转载请注明,转自[https://www.cnblogs.com/andy-songwei/p/10998855.html]谢谢! 在自定义View中,经常需要处理Android事件分发的问题, ...

随机推荐

  1. T-Sql(四)表关联和视图(view)

    今天讲下T-sql中用于查询的表关联和视图,我们平时做项目的时候会遇到一些复杂的查询操作,比如有班级表,学生表,现在要查询一个学生列表,要求把学生所属班级名称也查询出来,这时候简单的select查询就 ...

  2. js ES6 对字符的操作注意事项

    1.codePointAt方法是测试一个字符由两个字节还是由四个字节组成的最简单方法. function is32Bit(c) { return c.codePointAt(0) > 0xFFF ...

  3. 磁盘操作系统(DOS)

    一.概要 Disk Operation System(DOS)即磁盘操作系统,与win7一样是一个操作系统,字符界面,单用户,单任务. dos,是磁盘操作系统的缩写,是个人计算机上的一类操作系统.从1 ...

  4. 尽量使用translate而不是改变top/left进行动画(翻译)

    前言 本文翻译自 Why Moving Elements With Translate() Is Better Than Pos:abs Top/left,本文有改动,添加了一些作者自己的理解,不当之 ...

  5. flex布局示例

    来自:授权地址 作者:水牛01248 几个横排元素在竖直方向上居中 display: flex; flex-direction: row;//横向排列 align-items: center;//垂直 ...

  6. Solr学习总结(三)Solr web 管理后台

    前面讲到了Solr的安装,按道理,这次应该讲讲.net与数据库的内容,C#如何操作Solr索引等.不过我还是想先讲一些基础的内容,比如solr查询参数如何使用,各个参数都代表什么意思? 还有solr ...

  7. RabbitMQ原理与相关操作(三)消息持久化

    现在聊一下RabbitMQ消息持久化: 问题及方案描述 1.当有多个消费者同时收取消息,且每个消费者在接收消息的同时,还要处理其它的事情,且会消耗很长的时间.在此过程中可能会出现一些意外,比如消息接收 ...

  8. 发布 Rafy 源码到 GitHub

      最近项目组开始使用 Git 来作为源码管理.我今天就顺便把 Rafy 的源码也迁移到了 github 上,方便大家使用.这是项目的地址:https://github.com/zgynhqf/raf ...

  9. 说说&和&&的区别

    &和&&都可以用作逻辑与的运算符,表示逻辑与(and),当运算符两边的表达式的结果都为true 时,整个运算结果才为true,否则,只要有一方为false,则结果为false. ...

  10. C#封装程序集自定义类方法注释提示

    一.为什么使用封装程序集: 在很多分布式应用程序开发中,针对每一种功能可能条用的接口不一样,往往习惯将需要被调用的接口,封装成DLL给调用方应用后使用,这样既规范了调用的方式,又避免了调用出现参数请求 ...