本文主要包括以下内容

  1. view的事件分发
  2. viewGroup的事件分发

首先来看两张图

在执行touch事件时

  1. 首先执行dispatchTouchEvent方法,执行事件分发。

  2. 再执行onInterceptTouchEvent方法,判断是否中断事件,返回true时中断,执行自己的onTouchEvnet方法.

  3. 最后执行onTouchEvent方法,处理事件

View的事件分发

不管是DOWN,MOVE,UP都会按照下面的顺序执行:

1、dispatchTouchEvent

2、 setOnTouchListener的onTouch

3、onTouchEvent

其中

如果我们设置了setOnTouchListener,并且return true,那么View自己的onTouchEvent就不会被执行了

总结

1、整个View的事件转发流程是:

View.dispatchEvent->View.setOnTouchListener->View.onTouchEvent

在dispatchTouchEvent中会进行OnTouchListener的判断,如果OnTouchListener不为null且返回true,则表示事件被消费,onTouchEvent不会被执行;否则执行onTouchEvent。

2、onTouchEvent中的DOWN,MOVE,UP

  • DOWN时:

    a、首先设置标志为PREPRESSED,设置mHasPerformedLongPress=false ;然后发出一个115ms后的mPendingCheckForTap;

    b、如果115ms内没有触发UP,则将标志置为PRESSED,清除PREPRESSED标志,同时发出一个延时为500-115ms的,检测长按任务消息;

    c、如果500ms内(从DOWN触发开始算),则会触发LongClickListener:

此时如果LongClickListener不为null,则会执行回调,同时如果LongClickListener.onClick返回true,才把mHasPerformedLongPress设置为true;否则mHasPerformedLongPress依然为false;

  • MOVE时:

    主要就是检测用户是否划出控件,如果划出了:

    115ms内,直接移除mPendingCheckForTap;

    115ms后,则将标志中的PRESSED去除,同时移除长按的检查:removeLongPressCallback();

  • UP时:

    a、如果115ms内,触发UP,此时标志为PREPRESSED,则执行UnsetPressedState,setPressed(false);会把setPress转发下去,可以在View中复写dispatchSetPressed方法接收;

    b、如果是115ms-500ms间,即长按还未发生,则首先移除长按检测,执行onClick回调;

    c、如果是500ms以后,那么有两种情况:

    i.设置了onLongClickListener,且onLongClickListener.onClick返回true,则点击事件OnClick事件无法触发;

    ii.没有设置onLongClickListener或者onLongClickListener.onClick返回false,则点击事件OnClick事件依然可以触发;

    d、最后执行mUnsetPressedState.run(),将setPressed传递下去,然后将PRESSED标识去除;

最后问个问题,然后再运行个例子结束:

1、setOnLongClickListener和setOnClickListener是否只能执行一个

不是的,只要setOnLongClickListener中的onClick返回false,则两个都会执行;返回true则会屏幕setOnClickListener

Android ViewGroup事件分发机制

大体的事件流程为:

MyLinearLayout的dispatchTouchEvent -> MyLinearLayout的onInterceptTouchEvent -> MyButton的dispatchTouchEvent ->Mybutton的onTouchEvent

可以看出,在View上触发事件,最先捕获到事件的为View所在的ViewGroup,然后才会到View自身~

1、ACTION_DOWN中,ViewGroup捕获到事件,然后判断是否拦截,如果没有拦截,则找到包含当前x,y坐标的子View,赋值给mMotionTarget,然后调用 mMotionTarget.dispatchTouchEvent

2、ACTION_MOVE中,ViewGroup捕获到事件,然后判断是否拦截,如果没有拦截,则直接调用mMotionTarget.dispatchTouchEvent(ev)

3、ACTION_UP中,ViewGroup捕获到事件,然后判断是否拦截,如果没有拦截,则直接调用mMotionTarget.dispatchTouchEvent(ev)

当然了在分发之前都会修改下坐标系统,把当前的x,y分别减去child.left 和 child.top ,然后传给child;

关于拦截

如何拦截

复写ViewGroup的onInterceptTouchEvent方法:

@Override
public boolean onInterceptTouchEvent(MotionEvent ev)
{
int action = ev.getAction();
switch (action)
{
case MotionEvent.ACTION_DOWN:
//如果你觉得需要拦截
return true ;
case MotionEvent.ACTION_MOVE:
//如果你觉得需要拦截
return true ;
case MotionEvent.ACTION_UP:
//如果你觉得需要拦截
return true ;
} return false;
}

默认是不拦截的,即返回false;如果你需要拦截,只要return true就行了,这要该事件就不会往子View传递了,并且如果你在DOWN retrun true ,则DOWN,MOVE,UP子View都不会捕获事件;如果你在MOVE return true , 则子View在MOVE和UP都不会捕获事件。

原因很简单,当onInterceptTouchEvent(ev) return true的时候,会把mMotionTarget 置为null ;

如何不被拦截

如果ViewGroup的onInterceptTouchEvent(ev) 当ACTION_MOVE时return true ,即拦截了子View的MOVE以及UP事件;

此时子View希望依然能够响应MOVE和UP时该咋办呢?

Android给我们提供了一个方法: requestDisallowInterceptTouchEvent(boolean) 用于设置是否允许拦截,我们在子View的dispatchTouchEvent中直接这么写:

@Override
public boolean dispatchTouchEvent(MotionEvent event)
{
getParent().requestDisallowInterceptTouchEvent(true);
int action = event.getAction(); switch (action)
{
case MotionEvent.ACTION_DOWN:
Log.e(TAG, "dispatchTouchEvent ACTION_DOWN");
break;
case MotionEvent.ACTION_MOVE:
Log.e(TAG, "dispatchTouchEvent ACTION_MOVE");
break;
case MotionEvent.ACTION_UP:
Log.e(TAG, "dispatchTouchEvent ACTION_UP");
break; default:
break;
}
return super.dispatchTouchEvent(event);
}

getParent().requestDisallowInterceptTouchEvent(true); 这样即使ViewGroup在MOVE的时候return true,子View依然可以捕获到MOVE以及UP事件。

如果ViewGroup在onInterceptTouchEvent(ev) ACTION_DOWN里面直接return true了,那么子View是木有办法的捕获事件的~~~

如果没有找到合适的子View

1、ACTION_DOWN的时候,子View.dispatchTouchEvent(ev)返回的为false ;

则不处理,向上传递,由父view处理

总结

关于代码流程上面已经总结过了~

1、如果ViewGroup找到了能够处理该事件的View,则直接交给子View处理,自己的onTouchEvent不会被触发;

2、可以通过复写onInterceptTouchEvent(ev)方法,拦截子View的事件(即return true),把事件交给自己处理,则会执行自己对应的onTouchEvent方法

3、子View可以通过调用 getParent().requestDisallowInterceptTouchEvent(true); 阻止ViewGroup对其MOVE或者UP事件进行拦截;

好了,那么实际应用中能解决哪些问题呢?

比如你需要写一个类似slidingmenu的左侧隐藏menu,主Activity上有个Button、ListView或者任何可以响应点击的View,你在当前View上死命的滑动,菜单栏也出不来;因为MOVE事件被子View处理了~ 你需要这么做:在ViewGroup的dispatchTouchEvent中判断用户是不是想显示菜单,如果是,则在onInterceptTouchEvent(ev)拦截子View的事件;自己进行处理,这样自己的onTouchEvent就可以顺利展现出菜单栏了~

参考链接

Android View 事件分发机制 源码解析 (上) - Hongyang - 博客频道 - CSDN.NET

Android ViewGroup事件分发机制 - Hongyang - 博客频道 - CSDN.NET

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

  1. Android View 事件分发机制 源码解析 (上)

    一直想写事件分发机制的文章,不管咋样,也得自己研究下事件分发的源码,写出心得~ 首先我们先写个简单的例子来测试View的事件转发的流程~ 1.案例 为了更好的研究View的事件转发,我们自定以一个My ...

  2. android的事件分发机制理解

    android的事件分发机制理解 1.事件触发主要涉及到哪些层面的哪些函数(个人理解的顺序,可能在某一层会一次回调其它函数) activity中的dispatchTouchEvent .layout中 ...

  3. android view事件分发机制

    首先我们先写个简单的例子来测试View的事件转发的流程~ 1.案例 为了更好的研究View的事件转发,我们自定以一个MyButton继承Button,然后把跟事件传播有关的方法进行复写,然后添加上日志 ...

  4. Android View 事件分发机制 源代码解析 (上)

    一直想写事件分发机制的文章,无论咋样,也得自己研究下事件分发的源代码.写出心得~ 首先我们先写个简单的样例来測试View的事件转发的流程~ 1.案例 为了更好的研究View的事件转发,我们自定以一个M ...

  5. Android Touch事件分发机制学习

    Android  事件分发机制 ViewGroup dispatchTouchEvent 返回true dispatchTouchEvent: Activity ACTION_DOWN Myrelat ...

  6. Android View 事件分发机制详解

    想必很多android开发者都遇到过手势冲突的情况,我们一般都是通过内部拦截和外部拦截法解决此类问题.要想搞明白原理就必须了解View的分发机制.在此之前我们先来了解一下以下三个非常重要的方法: di ...

  7. 浅谈Android View事件分发机制

    引言 前面的文章介绍了View的基础知识和View的滑动,今天我们来介绍View的另一个核心知识,View的事件分发机制. 点击事件的传递规则 所谓的点击事件的分发机制,其实就是对MotionEven ...

  8. Android开发——事件分发机制详解

    0. 前言   转载请注明出处:http://blog.csdn.net/seu_calvin/article/details/52566965 深入学习事件分发机制,是为了解决在Android开发中 ...

  9. Android的事件分发机制

    public boolean dispatchTouchEvent(MotionEvent event) 通过方法名我们不难猜测,它就是事件分发的重要方法.那么很明显,如果一个MotionEvent传 ...

随机推荐

  1. Codeforces 567D One-Dimensional Battle Ships

    传送门 D. One-Dimensional Battle Ships time limit per test 1 second memory limit per test 256 megabytes ...

  2. jQuery返回顶部(精简版)

    jQuery返回顶部(精简版) <!DOCTYPE html><html lang="en"><head> <meta charset=& ...

  3. 领域模型中的实体类分为四种类型:VO、DTO、DO、PO

    http://kb.cnblogs.com/page/522348/ 由于不同的项目和开发人员有不同的命名习惯,这里我首先对上述的概念进行一个简单描述,名字只是个标识,我们重点关注其概念: 概念: V ...

  4. C# ManualResetEvent 的方法介绍

        名称 说明 1. Close 在派生类中被重写时,释放由当前 WaitHandle 持有的所有资源. (继承自 WaitHandle.)在XNA Framework中,此成员由 Close() ...

  5. Hibernate unsaved-value 属性

    Session的saveOrUpdate方法是由Hibernate来判断被操作对象究竟是一个持久化对象还是临时自由状态对象.这需要在对象映射文件的主键id中定义unsaved-value属性,如果不显 ...

  6. 10.23lamp环境

    前序: 查考文章:http://www.cnblogs.com/mchina/archive/2012/11/28/2778779.html http://www.centos.bz/2011/09/ ...

  7. bootshrap会改变IE浏览器滚动条样式

    在某个小网站的开发中 客户一直抱怨在IE11中网页右边滚动条不一样 后来发现在IE11中,有2个页面滚动条会自动隐藏,一开始以为是浏览器默认行为,改了overflow:scroll后也没有用.仔细观察 ...

  8. 每日一练(写不出心得体会了!毕竟哪有那么多心得好写。然后看github上有很多不错的题目。分享一下!)

    第一题: 问题描述:写一个reverseWords函数 调用方式:console.log(reverseWords('Hello World')); 期望输出:World Hello 第二题: 问题描 ...

  9. PHP数字格式化,每三位逗号分隔数字,可以保留小数

    在报价的时候为了给浏览者更清晰明确的数字,所以需要用到数字格式化,有两种方法,一种自己写函数,另一种当然是系统自带的,其实我更喜欢系统自带的. 先来系统简单的: string number_forma ...

  10. IIS假死状态处理

    为应用程序池 'DefaultAppPool' 提供服务的进程关闭时间超过了限制  服务器经常产生“应用程序池 'DefaultAppPool' 提供服务的进程关闭时间超过了限制.进程 ID 是 '2 ...