您的位置:首页 > 移动开发 > Android开发

android ViewGroup的事件分发机制

2015-09-21 23:20 465 查看
android 事件分发机制的理解

Android 事件分发机制一直让我很混乱,最近拼了,仔细研读源码,有了一些自己的认识,今天记下.梳理下知识.

学习事件分发机制,就必须先了解一个对象. MontionEvent. 该对象分装了用户触碰屏膜这一事件.

系统有一个线程在循环收集屏幕硬件信息,当用户触摸屏幕时,该线程会把从硬件设备收集到的信息封装成一个MotionEvent对象,然后把该对象存放到一个消息队列中。

系统的另一个线程循环的读取消息队列中的MotionEvent,然后交给WMS去派发,WMS把该事件派发给当前处于活动的Activity,即处于活动栈最顶端的Activity。

这就是一个先进先出的消费者和生产者的模板,一个线程不停的创建MotionEvent对象放入队列中,另一个线程不断的从队列中取出MotionEvent对象进行分发。

当用户的手指从接触屏幕到离开屏幕,是一个完整的触摸事件,在该事件中,系统会不断收集事件信息封装成MotionEvent对象。收集的间隔时间取决于硬件设备,

例如屏幕的灵敏度以及cpu的计算能力。目前的手机一般在20毫秒左右。

MontionEvent详解:

MotionEvent对象包含了触摸事件的时间、位置、面积、压力、以及本次事件的Dwon发生的时间。

MotionEvent常用的Action分为5种:Down 、Up、Move、Cancel、OutSide

MotionEvent中我们常用的方法就是获取点击的坐标,因为这是与我们操作息息相关的。获取坐标有两种方式:

getX和getY用于获取以该View左上角为坐标原点的坐标

getRowX和getRowY用于获取以屏幕左上角为坐标原点的坐标

事件分发机制的粗粒度流程分析

我们知道一个activity的布局视图树的根节点是DecorView(实际上就是个FrameLayout).MontionEvent实现了Parcelable接口.所以系统服务可以跨进程将该对象传给我们应用中的处于最顶端的Activity的

视图树的根节点DecorView.DecorView的dispatchTouchEvent()开始下发事件.

这里有一个和其他事件传递不同的地方,DecorView会优先传递给Activity,而不是它的子View。而Activity如果不处理又会回传给DecorView,DecorView才会再将事件传给子View。

dispatchTouchEvent就是触摸事件传递的对外接口,无论是DecorView传给Activity,还是ViewGroup传递给子View,都是直接调用对方的dispatchTouchEvent方法,并传递MotionEvent参数。

粗粒度的流程讲完了.让我们具体到view与ViewGroup里来看他们的事件分发机制吧.

View 的事件分发比较简单,因不包含子View.看其源码

public boolean disPatchTouchEvent(MotionEvent ev){

监听是否注册了touchListener 是否是可用的(可以获取焦点被点击和触碰的view) tounch监听是否消费了该事件

if(mTouchListener!=null&&(myViewFlags&ENABLE_FALAG)==ENABLED&&mTouchListener.onTouch(this,ev)){

以上三个条件都成立则说明事件传递给了view的tounchListener来处理.并终结.

return true

}

事件交由View的OnTouchEvent来处理.结果在上传给其上层的ViewGroup

return onTounchEvent(ev)

}

首先判断是否设置了触摸监听,并且可以响应事件,就交由监听的onTouch处理。

如果上述条件不成立,或者监听的onTouch事件没有消费掉该事件,则交由onTouchEvent进行处理,并把返回结果交给上层。

Viewgroup 的事件分发

Down事件的分发:

我会一步步的解释源码将其步骤列出.

1.通过参数MontionEvent 获取事件的布局并将其转换为视图坐标以便于在事件传递时传递坐标给子view.

2.将mMontionTarget(View)置为空.因为在down事件分发的时候会将事件传递给view,而在其他类型事件(move,cancle...)时的分发时,就不必在通过遍历view树了.直接将事件分发给

将mMontionTarget.这里将其置为空,以便遍后面记录down事件接受事件的view.

3.通过一个if判断来决定是否分发事件.if(disallowIntcept||!onIntceptTouchEvent()) disallowIntcept是表示viewGroup默认是否拦截事件.其默认值是false.onIntceptTouchEvent()默认

也是返回false,所以viewGroup默认是会向下分发事件.当然我们也可以复写onIntceptTouchEvent(),来拦截事件.

4.分发事件,开始遍历viewGroup的子view.注意是倒着遍历子view的.事件会先分发给后加入的view.

5.遍历中(for循环中)判断当前的的view是否可见,是否有动画(可见或有动画的view是可以接受点击事件的)

6.判断事件的布局坐标是否在当前view的坐标范围内.

7.在将视图坐标转变为布局坐标.以便后面付给这个view.

8.又一个判断 if(child.disPatchTouchEvent(ev)){mMontionTarget=child,return true;}

这个是最关键的一步.判断里,child如果是viewGroup,就递归走上面的代码,继续分发事件,事件或者被viewgroup给拦截消费向上传.或不拦截继续往下传或许是view分发处理在往上传给父view的onTouch().

上传过程中或许事件中途被消费或传到最顶端消失.

其他事件:

直接将事件传递给down事件传递时的的mMontionTargetdisPatchTouchEvent.以下是跟专业详细的说法:

判断事件是否被取消或者事件是否要拦截住,是的话,给Down事件找到的target发送一个取消事件。

如果不取消,也不拦截,并且Down已经找到了target,则直接交给target处理,不再遍历子View寻找合适的View了。

这种处理事件是正确的,我们用手机经常可以体会到,当我手指按在一个拖动条上之后,在拖动的时候手指就算移出了拖动条,依然会把事件分发给拖动条控制它的拖动。

在对viewGroup和View的事件分发机制有了详细的了解后,我们现在重现一下当用户触碰了一下Android手机后整个事件分发的过程.

系统有windowManger的服务一直在后台运行.有两种线程,一种不断的手机屏幕被触碰的事件,可以称为采样线程.一般手机每20mm就去收集一次放入消息队列中.touch事件有多种类型,up,move,cancle等.另一中则

个线程则不断从消息队列中取出事件进行分发.事件会被分发给任务栈最顶端,即与用户互动的activity的DecorView(即FrameLayout).这里注意事件不会直接从DecorView开始向下分发,而是先

传递给activity.如果activity的disPatchTouchEvent()返回false则事件回传给DecorView的disPatchTouchEvent()开始进行事件分发.而activity的onTouchevent()会是最后一个获得事件.当然前提

是事件没有被某个view消费.

DecorView 的disPatchTouchEvent()开始遵照viewGroup的事件分发机制开始分发事件详见上面的类容.

从view及viewGroup的事件分发机制我们可以看出我们可以复写viewGroup的onIntceptTouchEvent()来拦截事件.复写onTouchEvent()来处理事件.当然onTouch里也可以.这里有研究必要的是OnTouchEvent()的源码

在OnTouchEvent(){onTouch是由程序员自己实现无源码研究}.OnTouchEvent主要处理了click longClick 和cancle等一般性事件.我们色置的clicklistener,也在这里被调用.具体怎样处理日后研究.

注意:一:只要view的onIntceptTouchEvent()返回false,事件就不会传递给其子view 不管其onTouch()返回是否是true.如果onTouch返回true向上传递false事件消费.

二:viewGroup也可以设置OnTouchListener 其onTouch()会在子View()的onTouchEvent()执行完后由内往外执行.如果子view是view则还要在其onTouch()执行完后执行.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: