Android事件分发原理全面解析

来源:互联网 发布:桌面软件下载大全 编辑:程序博客网 时间:2024/06/04 19:45

       转载请注明出处:  http://blog.csdn.net/geekluxun/article/details/52690051    

      这是本人的第一篇博客,之前都是看着很多大牛的博客学习成长起来的,以后决定自己也来写些技术博客,一是为了和各位童鞋交流技术,共同成长;二是为了把自己平时所学的知识点以文字的形式记录一下,方便以后查阅。好了废话少说,开始我的博客之旅……

      今天给大家介绍的是有关Android事件分发机制,主要从以下五个方面介绍今天的主题

      【一】、事件

      【二】、事件分发几个核心概念

      【三】、事件分发涉及到的方法解析

      【四】、事件分发流程解析

      【五】、事件相关其他零碎知识点


一、事件

      在Android开发中,涉及到事件概念 事件指用户动作的集合,主要包括点按、长按、拖拽、滑动等。总的来说,所有的事件都由以下三个部分作为基础:

      按下(ACTION_DOWN)

      移动(ACTION_MOVE)

      抬起(ACTION_UP)


二、事件分发几个核心概念

      事件分发

      又叫事件传递,简单的说就是把事件(用户动作)传递给哪个View来处理。

大家会问为什么会存在事件分发?在Android中所有的视图都是继承自View和ViewGroup这两个类,View指的是无法包含其他视图的控件,ViewGroup指的是容器,可以包含其他View或者ViewGroup的控件。这样,用户动作的那个点(坐标)就有可能是多个视图的叠加位置,Android为了区分究竟哪个视图(控件)处理此次的用户事件,就存在一个分发逻辑。说白点就是给谁处理。

      事件处理

      指的是对用户动作作出的响应,在接下来介绍的几个方法中会做详细讲述。

      事件截断

      指父View对事件的传递的终止或者叫截断,一旦截断,事件传递就会停止。


三、事件分发涉及到的主要方法

public boolean dispatchTouchEvent(MotionEvent event)public boolean onTouch(View v, MotionEvent event)public boolean onTouchEvent(MotionEvent event)public void onClick(View v)public boolean onInterceptTouchEvent(MotionEvent ev)

大家发现上面几个方法除了onClick()外其他方法都是返回boolean,

true:表示事件被当前View或ViewGroup处理或者截断。

false:表示未处理,继续传递下去。


下面就每个方法详细介绍:

public boolean dispatchTouchEvent(MotionEvent event)

这个方法表示事件是否继续往下分发。分发的顺序遵循从外往内,即最外层的view最先传递到事件,依次类推......

返回值

true:事件停止分发,包括当前view自己也不会做任何处理

false: 事件继续向下分发

public boolean onTouch(View v, MotionEvent event)

这个方法是在dispatchTouchEvent中调用的,是提供给用户处理事件的回调接口。

此方法能调用的前提:

1、用户实现了View.OnTouchListener接口

2、用户注册了监听器onTouchListener,即setOnTouchListener(this)

3、当前view是enable状态

public boolean onTouchEvent(MotionEvent event)

这个方法是在dispatchTouchEvent中调用的,但在onTouch方法之后(前提是能调用onTouch方法)

我们看下Android源码片段: 

public boolean dispatchTouchEvent(MotionEvent event) {   if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK)== ENABLED &&           mOnTouchListener.onTouch(this, event)) {       return true;    }   return onTouchEvent(event);}

很清楚上面提到的有关onTouch和onTouchEvent的关系,如果onTouch返回ture,则onTouchEvent不会调用,可以理解为onTouch消费了事件,事件停止传递。

public void onClick(View v)

这个方法是在onTouchEvent中调用的

调用的前提包括:

1、用户实现了OnClickListener接口

2、用户注册了OnClick监听器,即setOnClickListener()
3、onTouchEvent中事件序列为MotionEvent.ACTION_DOWN-->MotionEvent.ACTION_UP,

换一句话说,当前view在onTouchEvent中必须处理了ACTION_DOWN 和 ACTION_UP 事件(返回值为ture),所以我们知道,onClick是在onTouch之后调用的。 

public boolean onInterceptTouchEvent(MotionEvent ev)

这个方法是ViewGroup中特有的方法,View没有,可以用来截断事件传递

返回值

ture:截断事件传递,事件留给自己消费

false:不截断

如果当前View(准确的应该说是ViewGroup)截断了事件传递,接下来会按照onTouch->onTouchEvent->onClick顺序执行。


四、事件分发流程

      下面是我画的事件分发流程图,几点说明:

      【1】、事件要么被消费(处理)要么被截断onInterceptTouchEvent()中),要么被结束(dispatchTouchEvent()中)

      【2】、图中橙色箭头和方法是三种消费事件的情况,包括onTouch() 、onClick()、onTouchEvent()

      【3】、事件传递流程是一个递归过程,先从activity-->father view--> child view(由外到里)。在递归链上出现消费、截断、结束三种情况中的任一种,递归结束。如果递归到最里层view还未有任何主体消费事件,则事件传递路径沿着原路返回传递。即child view-->father view--> activity(由里往外),直到activity。


极端情况下事件没被任何Activity 或者 view消费,调用流程如下:

例如用户点击 View1区域,

打印的日志如下:


      大家看日志的最后两行发现ACTION_UP只在Activity中,不再传递下去了,这是因为没有任何主体消费ACTION_DOWN事件,接下来的所有事件不再传递下去。所有的事件序列都是开始于ACTION_DOWN。


下面是详细的事件分发流程图:





五、其他零碎知识点

1、onTouch 和onTouchEvent区别

      前面已经介绍,onTouch的调用是优先于onTouchEvent的,他们和onClick组成消费事件的三叉戟,

      两者的区别在于:

      【1】、onTouch是View提供给用户处理事件的接口,onTouchEvent是View提供给子类处理事件的接口,表现在onTouch可以用在实现OnTouchListener接口的任何Activity、View、ViewGroup,onTouchEvent只能用于当前view 及其子类

      【2】、方法参数上onTouch比onTouchEvent多一个参数View,用户可以通过此参数过滤不同的view来做不同的处理(常见用法是在activity中复写onTouch)

      【3】、onTouch优先于onTouchEvent处理 

2、onKey()、onLongClick()onKeyDown(int,KeyEvent)onKeyUp(int,KeyEvent) 用法

      这几个方法用法和onClick类似,流程也和onClick一样,不再赘述 

3、Activity.dispatchTouchEvent(MotionEvent):此方法允许 Activity 在分派给窗口之前截获所有触摸事件。另外,activity中也有onTouchEvent 方法,使用和View、ViewGroup中类似

4ViewParent.requestDisallowInterceptTouchEvent(boolean) 对父视图调用此方法表明不应使用onInterceptTouchEvent(MotionEvent) 截获触摸事件  

5、方法返回值说明

super.dispatchTouchEvent(ev) super.onTouchEvent(event) super.onInterceptTouchEvent(ev);
如果父类(super)是View或者ViewGroup,则以上几个方法都返回false。


0 0
原创粉丝点击