Android View的事件分发机制剖析Word文档下载推荐.docx

上传人:b****3 文档编号:18409868 上传时间:2022-12-16 格式:DOCX 页数:14 大小:21.68KB
下载 相关 举报
Android View的事件分发机制剖析Word文档下载推荐.docx_第1页
第1页 / 共14页
Android View的事件分发机制剖析Word文档下载推荐.docx_第2页
第2页 / 共14页
Android View的事件分发机制剖析Word文档下载推荐.docx_第3页
第3页 / 共14页
Android View的事件分发机制剖析Word文档下载推荐.docx_第4页
第4页 / 共14页
Android View的事件分发机制剖析Word文档下载推荐.docx_第5页
第5页 / 共14页
点击查看更多>>
下载资源
资源描述

Android View的事件分发机制剖析Word文档下载推荐.docx

《Android View的事件分发机制剖析Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《Android View的事件分发机制剖析Word文档下载推荐.docx(14页珍藏版)》请在冰豆网上搜索。

Android View的事件分发机制剖析Word文档下载推荐.docx

源码如下:

publicbooleandispatchTouchEvent(MotionEventev){

if(ev.getAction()==MotionEvent.ACTION_DOWN){

onUserInteraction();

}

if(getWindow().superDispatchTouchEvent(ev)){

returntrue;

returnonTouchEvent(ev);

}

如果当前事件是down的话,就调用onUserInteraction方法,onUserInteraction是一个空方法,我们可以暂时不搭理。

然后调用getWindow方法获取到当前Activity关联的Window,Window再调用superDispatchTouchEvent方法将事件传入进行分发。

如果superDispatchTouchEvent方法返回true的话,view已经处理了事件。

整个事件循环结束。

如果返回false,没有view处理这个事件。

事件往上抛,那就Activity自己处理了,即Activity的onTouchEvent方法会被调用。

因为想要知道事件的整个分发过程,现在关注的是Window的superDispatchTouchEvent方法,那么就跟进去看看:

publicabstractbooleansuperDispatchTouchEvent(MotionEventevent);

Window是一个抽象类,superDispatchTouchEvent是一个抽象的方法,那么我们必须要找到window的实现类才行,可是茫茫人海怎么找呢?

看到window类的说明就明白了

*<

p>

Theonlyexistingimplementationofthisabstractclassis

*android.view.PhoneWindow,whichyoushouldinstantiatewhenneedinga

*Window.

*/

publicabstractclassWindow

意思是Window存在唯一的实现是android.view.PhoneWindow

那么PhoneWindow里的superDispatchTouchEvent方法就是我们要找的信息,如下:

@Override

publicbooleansuperDispatchTouchEvent(MotionEventevent){

returnmDecor.superDispatchTouchEvent(event);

直接将事件传递给了DecorView。

这时事件已经是到达View了哦。

那么跟进DecorView的superDispatchTouchEvent方法看看,如下:

returnsuper.dispatchTouchEvent(event);

内部调用了父类的dispatchTouchEvent方法,那么DecorView的父类是什么呢?

DecorView肯定是View的,那么刚才开篇提到,我们通过setContentView设置的View,是DecorView的子View。

那么更加准确的说DecorView是一个ViewGroup。

privatefinalclassDecorViewextendsFrameLayoutimplementsRootViewSurfaceTaker

可以看到DecorView是继承自FrameLayout,FrameLayout是ViewGroup,也就是说DecorView是一个ViewGroup。

那么现在只需要关注ViewGroup的dispatchTouchEvent方法。

继续前进

ViewGroup的事件分发

ViewGroup的dispatchTouchEvent方法如下:

//代码省略

//Checkforinterception.

finalbooleanintercepted;

if(actionMasked==MotionEvent.ACTION_DOWN

||mFirstTouchTarget!

=null){

finalbooleandisallowIntercept=(mGroupFlags&

FLAG_DISALLOW_INTERCEPT)!

=0;

if(!

disallowIntercept){

intercepted=onInterceptTouchEvent(ev);

ev.setAction(action);

//restoreactionincaseitwaschanged

}else{

intercepted=false;

//Therearenotouchtargetsandthisactionisnotaninitialdown

//sothisviewgroupcontinuestointercepttouches.

intercepted=true;

canceled&

&

!

intercepted){

finalintchildrenCount=mChildrenCount;

if(newTouchTarget==null&

childrenCount!

=0){

finalfloatx=ev.getX(actionIndex);

finalfloaty=ev.getY(actionIndex);

//Findachildthatcanreceivetheevent.

//Scanchildrenfromfronttoback.

finalArrayList<

View>

preorderedList=buildOrderedChildList();

finalbooleancustomOrder=preorderedList==null

&

isChildrenDrawingOrderEnabled();

finalView[]children=mChildren;

for(inti=childrenCount-1;

i>

i--){

finalintchildIndex=customOrder

?

getChildDrawingOrder(childrenCount,i):

i;

finalViewchild=(preorderedList==null)

children[childIndex]:

preorderedList.get(childIndex);

//Ifthereisaviewthathasaccessibilityfocuswewantit

//togettheeventfirstandifnothandledwewillperforma

//normaldispatch.Wemaydoadoubleiterationbutthisis

//safergiventhetimeframe.

if(childWithAccessibilityFocus!

=child){

continue;

childWithAccessibilityFocus=null;

i=childrenCount-1;

canViewReceivePointerEvents(child)

||!

isTransformedTouchPointInView(x,y,child,null)){

ev.setTargetAccessibilityFocus(false);

newTouchTarget=getTouchTarget(child);

if(newTouchTarget!

//Childisalreadyreceivingtouchwithinitsbounds.

//Giveitthenewpointerinadditiontotheonesitishandling.

newTouchTarget.pointerIdBits|=idBitsToAssign;

break;

resetCancelNextUpFlag(child);

if(dispatchTransformedTouchEvent(ev,false,child,idBitsToAssign)){

//Childwantstoreceivetouchwithinitsbounds.

mLastTouchDownTime=ev.getDownTime();

if(preorderedList!

//childIndexpointsintopresortedlist,findoriginalindex

for(intj=0;

j<

childrenCount;

j++){

if(children[childIndex]==mChildren[j]){

mLastTouchDownIndex=j;

mLastTouchDownIndex=childIndex;

mLastTouchDownX=ev.getX();

mLastTouchDownY=ev.getY();

newTouchTarget=addTouchTarget(child,idBitsToAssign);

alreadyDispatchedToNewTouchTarget=true;

//Theaccessibilityfocusdidn'

thandletheevent,soclear

//theflaganddoanormaldispatchtoallchildren.

=null)preorderedList.clear();

mFirstTouchTarget!

//Didnotfindachildtoreceivetheevent.

//Assignthepointertotheleastrecentlyaddedtarget.

newTouchTarget=mFirstTouchTarget;

while(newTouchTarget.next!

newTouchTarget=newTouchTarget.next;

//Dispatchtotouchtargets.

if(mFirstTouchTarget==null){

//Notouchtargetssotreatthisasanordinaryview.

handled=dispatchTransformedTouchEvent(ev,canceled,null,

TouchTarget.ALL_POINTER_IDS);

returnhandled;

代码比较长,一点一点分析,先看到一开始的判断

if(actionMasked==MotionEvent.ACTION_DOWN||mFirstTouchTarget!

=null)

mFirstTouchTarget!

=null的意义是ViewGroup不拦截事件并将事件交由子元素处理,先这样记着,这从后面的addTouchTarget方法可以得出结论的。

然后又会来到这个if判断。

if(!

那我们看看disallowIntercept。

而disallowIntercept的赋值过程中,有一个FLAG_DISALLOW_INTERCEPT标记位

finalbooleandisallowIntercept=(mGroupFlags&

这个FLAG_DISALLOW_INTERCEPT标记位是可以通过requestDisallowInterceptTouchEvent方法来设置的。

回到if(!

disallowIntercept)的判断,进入这个if判断后,就会来到

intercepted=onInterceptTouchEvent(ev);

调用onInterceptTouchEvent方法,询问ViewGroup是否拦截事件。

读到这里,可以回忆下开篇时铺垫的结论,对于ViewGroup,点击事件产生后,首先会传递给它,这时它的dispatchTouchEvent方法就会被调用,接着会调用它的onInterceptTouchEvent方法,如果这个ViewGroup的onInterceptTouchEvent方法返回true就表示它要拦截当前事件,接着事件就会交给这个ViewGroup处理,即它的onTouchEvent方法就会被调用。

如果返回false表示不拦截,通常ViewGroup也是不拦截事件的。

那现在先分析不拦截的情况,不拦截那就好办了的。

经过一系列的判断,就会来到一个for循环遍历。

for(inti=childrenCount-1;

i--)

这时ViewGroup开始分发传递事件,遍历子元素了。

首先肯定需要过滤掉一些无关点击事件的子元素的,判断子元素是否能够接收点击事件,点击事件的坐标是否落在子元素区域内。

如果不能够接收点击事件或者点击事件的坐标没有落在子元素区域,就会跳出当前循环,继续遍历下一个子元素。

这下就知道了Android系统为什么能够知道点击的是Button而不是TextView,其实内部就只是做了一个判断嘛。

那么继续分析,子元素符合以上两个条件后,就将事件传递给这个子元素。

会来到了这个判断。

if(dispatchTransformedTouchEvent(ev,false,child,idBitsToAssign))

执行dispatchTransformedTouchEvent方法,将子元素传进去。

这个方法很重要,那么跟进看看

/**

*Transformsamotioneventintothecoordinatespaceofaparticularchildview,

*filtersoutirrelevantpointerids,andoverridesitsactionifnecessary.

*Ifchildisnull,assumestheMotionEventwillbesenttothisViewGroupinstead.

privatebooleandispatchTransformedTouchEvent(MotionEventevent,booleancancel,

Viewchild,intdesiredPointerIdBits){

finalbooleanhandled;

//Cancelingmotionsisaspecialcase.Wedon'

tneedtoperformanytransformations

//orfiltering.Theimportantpartistheaction,notthecontents.

finalintoldAction=event.getAction();

if(cancel||oldAction==MotionEvent.ACTION_CANCEL){

event.setAction(MotionEvent.ACTION_CANCEL);

if(child==null){

handled=super.dispatchTouchEvent(event);

handled=child.dispatchTouchEvent(event);

event.setAction(oldAction);

我们看到child!

=null的情况,如果子元素不为空,调用子元素的dispatchTouchEvent方法继续分发事件,同时返回处理结果布尔值,这时就将事件传递到了子View处理。

完成了一轮的事件分发。

这个方法先到这里就好。

再看回ViewGroup的dispatchTouchEvent方法,如果dispatchTransformedTouchEvent方法返回true的话,这时事件已经传递给子元素处理,ViewGroup已经不管这个事件了。

那么就会进入if语句,最后会来到addTouchTarget方法,这个方法之前是提到过的,用于mFirstTouchTarget标记位的赋值。

那跟进这个方法看看

*Addsatouchtargetforspecifiedchildtothebeginningofthelist.

*Assumesthetargetchildisnotalreadypresent.

privateTouchTargetaddTouchTarget(Viewchild,intpointerIdBits){

TouchTargettarget=TouchTarget.obtain(child,pointerIdBits);

target.next=mFirstTouchTarget;

mFirstTouchTarget=target;

returntarget;

其实就是让mFirstTouchTarget指向子元素。

执行完这个addTouchTarget方法后,最终会到break语句,那么就会跳出整个for循环体。

ViewGroup结束分发过程!

又回到dispatchTransformedTouchEvent方法,如果dispatchTransformedTouchEvent方法返回false,那么if语句的一大段代码都不执行了,而是回到for循环继续遍历子元素进行分发。

如此重复完成事件的

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 人文社科 > 文化宗教

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1