Android View的事件分发机制剖析Word文档下载推荐.docx
《Android View的事件分发机制剖析Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《Android View的事件分发机制剖析Word文档下载推荐.docx(14页珍藏版)》请在冰豆网上搜索。
源码如下:
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循环继续遍历子元素进行分发。
如此重复完成事件的