【移动应用开发技术】Andriod 从源码的角度详解View,ViewGroup的Touch事件的分发机制_第1页
【移动应用开发技术】Andriod 从源码的角度详解View,ViewGroup的Touch事件的分发机制_第2页
【移动应用开发技术】Andriod 从源码的角度详解View,ViewGroup的Touch事件的分发机制_第3页
【移动应用开发技术】Andriod 从源码的角度详解View,ViewGroup的Touch事件的分发机制_第4页
【移动应用开发技术】Andriod 从源码的角度详解View,ViewGroup的Touch事件的分发机制_第5页
已阅读5页,还剩18页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

【移动应用开发技术】Andriod从源码的角度详解View,ViewGroup的Touch事件的分发机制

今天这篇文章主要分析的是Android的事件分发机制,采用例子加源码的方式让大家深刻的理解Android事件分发的具体情况,虽然网上很多Android的事件分发的文章,有些还写的不错,但是我还是决定写这篇文章,用我自己的思维方式来帮助大家理解Android事件分发,Android事件分发到底有多重要呢?相信很多Android开发者都明白吧,这个我就不介绍了,我也写了很多篇文章里面涉及到Android的事件处理的问题,可能不理解Android事件分发的朋友会有点难理解吧,不过没关系,相信看了这篇文章的你会对Android事件分发有进一步的理解。我这篇文章分析的源码是Android2.2的源码,也许你会说,干嘛不去分析最新的源码呢?我这里要解释一下,Android2.2的源码跟最新的源码在功能效果方面是一样的,只不过最新的源码相对于Android2.2来说更加健壮一些,Android2.2的事件处理的代码几乎都写在一个方法体里面,而最新的源码分了很多个方法写,如果用最新的源码调用方法会绕来绕去的,相信你看的也会晕,出于这个考虑,我就拿Android2.2的源码来给大家分析。ViewGroup的事件分发机制我们用手指去触摸Android手机屏幕,就会产生一个触摸事件,但是这个触摸事件在底层是怎么分发的呢?这个我还真不知道,这里涉及到操作硬件(手机屏幕)方面的知识,也就是Linux内核方面的知识,我也没有了解过这方面的东西,所以我们可能就往上层来分析分析,我们知道Android中负责与用户交互,与用户操作紧密相关的四大组件之一是Activity,所以我们有理由相信Activity中存在分发事件的方法,这个方法就是dispatchTouchEvent(),我们先看其源码吧publicbooleandispatchTouchEvent(MotionEventev){

//如果是按下状态就调用onUserInteraction()方法,onUserInteraction()方法

//是个空的方法,我们直接跳过这里看下面的实现

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

onUserInteraction();

}

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

returntrue;

}

//getWindow().superDispatchTouchEvent(ev)返回false,这个事件就交给Activity

//来处理,Activity的onTouchEvent()方法直接返回了false

returnonTouchEvent(ev);

}这个方法中我们还是比较关心getWindow()的superDispatchTouchEvent()方法,getWindow()返回当前Activity的顶层窗口Window对象,我们直接看WindowAPI的superDispatchTouchEvent()方法/**

*Usedbycustomwindows,suchasDialog,topassthetouchscreenevent

*furtherdowntheviewhierarchy.Applicationdevelopersshould

*notneedtoimplementorcallthis.

*

*/

publicabstractbooleansuperDispatchTouchEvent(MotionEventevent);这个是个抽象方法,所以我们直接找到其子类来看看superDispatchTouchEvent()方法的具体逻辑实现,Window的唯一子类是PhoneWindow,我们就看看PhoneWindow的superDispattEvent()publicbooleansuperDispatchTouchEvent(KeyEventevent){

returnmDecor.superDispatchTouchEvent(event);

}里面直接调用DecorView类的superDispatchTouchEvent方法,或许很多人不了解DecorView这个类,DecorView是PhoneWindow的一个final的内部类并且继承FrameLayout的,也是Window界面的最顶层的View对象,这是什么意思呢?别着急,我们接着往下看我们先新建一个项目,取名AndroidTouchEvent,然后直接用模拟器运行项目,MainActivity的布局文件为<RelativeLayoutxmlns:android="/apk/res/android"

xmlns:tools="/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

tools:context=".MainActivity">

<TextView

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_centerHorizontal="true"

android:layout_centerVertical="true"

android:text="@string/hello_world"/>

</RelativeLayout>利用hierarchyviewer工具来查看下MainActivity的View的层次结构,如下图我们看到最顶层就是PhoneWindow$DecorView,接着DecorView下面有一个LinearLayout,LinearLayout下面有两个FrameLayout/20140321101826812?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveGlhYW5taW5n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast上面那个FrameLayout是用来显示标题栏的,这个Demo中是一个TextView,当然我们还可以定制我们的标题栏,利用getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE,R.layout.XXX);xxx就是我们自定义标题栏的布局XML文件下面的FrameLayout是用来装载ContentView的,也就是我们在Activity中利用setContentView()方法设置的View,现在我们知道了,原来我们利用setContentView()设置Activity的View的外面还嵌套了这么多的东西我们来理清下思路,Activity的最顶层窗体是PhoneWindow,而PhoneWindow的最顶层View是DecorView,接下来我们就看DecorView类的superDispatchTouchEvent()方法publicbooleansuperDispatchTouchEvent(MotionEventevent){

returnsuper.dispatchTouchEvent(event);

}在里面调用了父类FrameLayout的dispatchTouchEvent()方法,而FrameLayout中并没有dispatchTouchEvent()方法,所以我们直接看ViewGroup的dispatchTouchEvent()方法/**

*{@inheritDoc}

*/

@Override

publicbooleandispatchTouchEvent(MotionEventev){

finalintaction=ev.getAction();

finalfloatxf=ev.getX();

finalfloatyf=ev.getY();

finalfloatscrolledXFloat=xf+mScrollX;

finalfloatscrolledYFloat=yf+mScrollY;

finalRectframe=mTempRect;

//这个值默认是false,然后我们可以通过requestDisallowInterceptTouchEvent(booleandisallowIntercept)方法

//来改变disallowIntercept的值

booleandisallowIntercept=(mGroupFlags&FLAG_DISALLOW_INTERCEPT)!=0;

//这里是ACTION_DOWN的处理逻辑

if(action==MotionEvent.ACTION_DOWN){

//清除mMotionTarget,每次ACTION_DOWN都很设置mMotionTarget为null

if(mMotionTarget!=null){

mMotionTarget=null;

}

//disallowIntercept默认是false,就看ViewGroup的onInterceptTouchEvent()方法

if(disallowIntercept||!onInterceptTouchEvent(ev)){

ev.setAction(MotionEvent.ACTION_DOWN);

finalintscrolledXInt=(int)scrolledXFloat;

finalintscrolledYInt=(int)scrolledYFloat;

finalView[]children=mChildren;

finalintcount=mChildrenCount;

//遍历其子View

for(inti=count-1;i>=0;i--){

finalViewchild=children[i];

//如果该子View是VISIBLE或者该子View正在执行动画,表示该View才

//可以接受到Touch事件

if((child.mViewFlags&VISIBILITY_MASK)==VISIBLE

||child.getAnimation()!=null){

//获取子View的位置范围

child.getHitRect(frame);

//如Touch到屏幕上的点在该子View上面

if(frame.contains(scrolledXInt,scrolledYInt)){

//offsettheeventtotheview'scoordinatesystem

finalfloatxc=scrolledXFloat-child.mLeft;

finalfloatyc=scrolledYFloat-child.mTop;

ev.setLocation(xc,yc);

child.mPrivateFlags&=~CANCEL_NEXT_UP_EVENT;

//调用该子View的dispatchTouchEvent()方法

if(child.dispatchTouchEvent(ev)){

//如果child.dispatchTouchEvent(ev)返回true表示

//该事件被消费了,设置mMotionTarget为该子View

mMotionTarget=child;

//直接返回true

returntrue;

}

//Theeventdidn'tgethandled,trythenextview.

//Don'tresettheevent'slocation,it'snot

//necessaryhere.

}

}

}

}

}

//判断是否为ACTION_UP或者ACTION_CANCEL

booleanisUpOrCancel=(action==MotionEvent.ACTION_UP)||

(action==MotionEvent.ACTION_CANCEL);

if(isUpOrCancel){

//如果是ACTION_UP或者ACTION_CANCEL,将disallowIntercept设置为默认的false

//假如我们调用了requestDisallowInterceptTouchEvent()方法来设置disallowIntercept为true

//当我们抬起手指或者取消Touch事件的时候要将disallowIntercept重置为false

//所以说上面的disallowIntercept默认在我们每次ACTION_DOWN的时候都是false

mGroupFlags&=~FLAG_DISALLOW_INTERCEPT;

}

//Theeventwasn'tanACTION_DOWN,dispatchittoourtargetif

//wehaveone.

finalViewtarget=mMotionTarget;

//mMotionTarget为null意味着没有找到消费Touch事件的View,所以我们需要调用ViewGroup父类的

//dispatchTouchEvent()方法,也就是View的dispatchTouchEvent()方法

if(target==null){

//Wedon'thaveatarget,thismeanswe'rehandlingthe

//eventasaregularview.

ev.setLocation(xf,yf);

if((mPrivateFlags&CANCEL_NEXT_UP_EVENT)!=0){

ev.setAction(MotionEvent.ACTION_CANCEL);

mPrivateFlags&=~CANCEL_NEXT_UP_EVENT;

}

returnsuper.dispatchTouchEvent(ev);

}

//这个if里面的代码ACTION_DOWN不会执行,只有ACTION_MOVE

//ACTION_UP才会走到这里,假如在ACTION_MOVE或者ACTION_UP拦截的

//Touch事件,将ACTION_CANCEL派发给target,然后直接返回true

//表示消费了此Touch事件

if(!disallowIntercept&&onInterceptTouchEvent(ev)){

finalfloatxc=scrolledXFloat-(float)target.mLeft;

finalfloatyc=scrolledYFloat-(float)target.mTop;

mPrivateFlags&=~CANCEL_NEXT_UP_EVENT;

ev.setAction(MotionEvent.ACTION_CANCEL);

ev.setLocation(xc,yc);

if(!target.dispatchTouchEvent(ev)){

}

//clearthetarget

mMotionTarget=null;

//Don'tdispatchthiseventtoourownview,becausewealready

//sawitwhenintercepting;wejustwanttogivethefollowing

//eventtothenormalonTouchEvent().

returntrue;

}

if(isUpOrCancel){

mMotionTarget=null;

}

//finallyoffsettheeventtothetarget'scoordinatesystemand

//dispatchtheevent.

finalfloatxc=scrolledXFloat-(float)target.mLeft;

finalfloatyc=scrolledYFloat-(float)target.mTop;

ev.setLocation(xc,yc);

if((target.mPrivateFlags&CANCEL_NEXT_UP_EVENT)!=0){

ev.setAction(MotionEvent.ACTION_CANCEL);

target.mPrivateFlags&=~CANCEL_NEXT_UP_EVENT;

mMotionTarget=null;

}

//如果没有拦截ACTION_MOVE,ACTION_DOWN的话,直接将Touch事件派发给target

returntarget.dispatchTouchEvent(ev);

}这个方法相对来说还是蛮长,不过所有的逻辑都写在一起,看起来比较方便,接下来我们就具体来分析一下我们点击屏幕上面的TextView来看看Touch是如何分发的,先看看ACTION_DOWN在DecorView这一层会直接调用ViewGroup的dispatchTouchEvent(),先看18行,每次ACTION_DOWN都会将mMotionTarget设置为null,mMotionTarget是什么?我们先不管,继续看代码,走到25行,

disallowIntercept默认为false,我们再看ViewGroup的onInterceptTouchEvent()方法publicbooleanonInterceptTouchEvent(MotionEventev){

returnfalse;

}直接返回false,继续往下看,循环遍历DecorView里面的Child,从上面的MainActivity的层次结构图我们可以看出,DecorView里面只有一个Child那就是LinearLayout,第43行判断Touch的位置在不在LinnearLayout上面,这是毫无疑问的,所以直接跳到51行,调用LinearLayout的dispatchTouchEvent()方法,LinearLayout也没有dispatchTouchEvent()这个方法,所以也是调用ViewGroup的dispatchTouchEvent()方法,所以这个方法卡在51行没有继续下去,而是去先执行LinearLayout的dispatchTouchEvent()LinearLayout调用dispatchTouchEvent()的逻辑跟DecorView是一样的,所以也是遍历LinearLayout的两个FrameLayout,判断Touch的是哪个FrameLayout,很明显是下面那个,调用下面那个FrameLayout的dispatchTouchEvent(),

所以LinearLayout的dispatchTouchEvent()卡在51也没继续下去继续调用FrameLayout的dispatchTouchEvent()方法,和上面一样的逻辑,下面的FrameLayout也只有一个Child,就是RelativeLayout,FrameLayout的dispatchTouchEvent()继续卡在51行,先执行RelativeLayout的dispatchTouchEvent()方法执行RelativeLayout的dispatchTouchEvent()方法逻辑还是一样的,循环遍历RelativeLayout里面的孩子,里面只有一个TextView,所以这里就调用TextView的dispatchTouchEvent(),TextView并没有dispatchTouchEvent()这个方法,于是找TextView的父类View,在看View的dispatchTouchEvent()的方法之前,我们先理清下上面这些ViewGroup执行dispatchTouchEvent()的思路,我画了一张图帮大家理清下(这里没有画出onInterceptTouchEvent()方法)/20140321160727093?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveGlhYW5taW5n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast上面的ViewGroup的Touch事件分发就告一段落先,因为这里要调用TextView(也就是View)的dispatchTouchEvent()方法,所以我们先分析View的dispatchTouchEvent()方法在将上面的继续下去View的Touch事件分发机制我们还是先看View的dispatchTouchEvent()方法的源码publicbooleandispatchTouchEvent(MotionEventevent){

if(mOnTouchListener!=null&&(mViewFlags&ENABLED_MASK)==ENABLED&&

mOnTouchListener.onTouch(this,event)){

returntrue;

}

returnonTouchEvent(event);

}在这个方法里面,先进行了一个判断第一个条件mOnTouchListener就是我们调用View的setTouchListener()方法设置的第二个条件是判断View是否为enabled的,View一般都是enabled,除非你手动设置为disabled第三个条件就是OnTouchListener接口的onTouch()方法的返回值了,如果调用了setTouchListener()设置OnTouchListener,并且onTouch()方法返回true,View的dispatchTouchEvent()方法就直接返回true,否则就执行View的onTouchEvent()并返回View的onTouchEvent()的值现在你了解了View的onTouchEvent()方法和onTouch()的关系了吧,为什么Android提供了处理Touch事件onTouchEvent()方法还要增加一个OnTouchListener接口呢?我觉得OnTouchListener接口是对处理Touch事件的屏蔽和扩展作用吧,屏蔽作用我就不举例介绍了,看上面的源码就知道了,我就说下扩展吧,比如我们要打印View的Touch的点的坐标,我们可以自定义一个View如下publicclassCustomViewextendsView{

publicCustomView(Contextcontext,AttributeSetattrs){

super(context,attrs);

}

publicCustomView(Contextcontext,AttributeSetattrs,intdefStyle){

super(context,attrs,defStyle);

}

@Override

publicbooleanonTouchEvent(MotionEventevent){

Log.i("tag","X的坐标="+event.getX()+"Y的坐标="+event.getY());

returnsuper.onTouchEvent(event);

}

}也可以直接对View设置OnTouchListener接口,在return的时候调用下v.onTouchEvent()view.setOnTouchListener(newOnTouchListener(){

@Override

publicbooleanonTouch(Viewv,MotionEventevent){

Log.i("tag","X的坐标="+event.getX()+"Y的坐标="+event.getY());

returnv.onTouchEvent(event);

}

});这样子也实现了我们所需要的功能,所以我认为OnTouchListener是对onTouchEvent()方法的一个屏蔽和扩展作用,假如你有不一样的理解,你也可以告诉我下,这里就不纠结这个了。我们再看View的onTouchEvent()方法publicbooleanonTouchEvent(MotionEventevent){

finalintviewFlags=mViewFlags;

if((viewFlags&ENABLED_MASK)==DISABLED){

return(((viewFlags&CLICKABLE)==CLICKABLE||

(viewFlags&LONG_CLICKABLE)==LONG_CLICKABLE));

}

//如果设置了Touch代理,就交给代理来处理,mTouchDelegate默认是null

if(mTouchDelegate!=null){

if(mTouchDelegate.onTouchEvent(event)){

returntrue;

}

}

//如果View是clickable或者longClickable的onTouchEvent就返回true,否则返回false

if(((viewFlags&CLICKABLE)==CLICKABLE||

(viewFlags&LONG_CLICKABLE)==LONG_CLICKABLE)){

switch(event.getAction()){

caseMotionEvent.ACTION_UP:

booleanprepressed=(mPrivateFlags&PREPRESSED)!=0;

if((mPrivateFlags&PRESSED)!=0||prepressed){

booleanfocusTaken=false;

if(isFocusable()&&isFocusableInTouchMode()&&!isFocused()){

focusTaken=requestFocus();

}

if(!mHasPerformedLongPress){

removeLongPressCallback();

if(!focusTaken){

if(mPerformClick==null){

mPerformClick=newPerformClick();

}

if(!post(mPerformClick)){

performClick();

}

}

}

if(mUnsetPressedState==null){

mUnsetPressedState=newUnsetPressedState();

}

if(prepressed){

mPrivateFlags|=PRESSED;

refreshDrawableState();

postDelayed(mUnsetPressedState,

ViewConfiguration.getPressedStateDuration());

}elseif(!post(mUnsetPressedState)){

mUnsetPressedState.run();

}

removeTapCallback();

}

break;

caseMotionEvent.ACTION_DOWN:

if(mPendingCheckForTap==null){

mPendingCheckForTap=newCheckForTap();

}

mPrivateFlags|=PREPRESSED;

mHasPerformedLongPress=false;

postDelayed(mPendingCheckForTap,ViewConfiguration.getTapTimeout());

break;

caseMotionEvent.ACTION_CANCEL:

mPrivateFlags&=~PRESSED;

refreshDrawableState();

removeTapCallback();

break;

caseMotionEvent.ACTION_MOVE:

finalintx=(int)event.getX();

finalinty=(int)event.getY();

//当手指在View上面滑动超过View的边界,

intslop=mTouchSlop;

if((x<0-slop)||(x>=getWidth()+slop)||

(y<0-slop)||(y>=getHeight()+slop)){

//Outsidebutton

removeTapCallback();

if((mPrivateFlags&PRESSED)!=0){

removeLongPressCallback();

mPrivateFlags&=~PRESSED;

refreshDrawableState();

}

}

break;

}

returntrue;

}

returnfalse;

}这个方法也是比较长的,我们先看第4行,如果一个View是disabled,并且该View是Clickable或者longClickable,onTouchEvent()就不执行下面的代码逻辑直接返回true,表示该View就一直消费Touch事件,如果一个enabled的View,并且是clickable或者longClickable的,onTouchEvent()会执行下面的代码逻辑并返回true,综上,一个clickable或者longclickable的View是一直消费Touch事件的,而一般的View既不是clickable也不是longclickable的(即不会消费Touch事件,只会执行ACTION_DOWN而不会执行ACTION_MOVE和ACTION_UP)Button是clickable的,可以消费Touch事件,但是我们可以通过setClickable()和setLongClickable()来设置View是否为clickable和longClickable。当然还可以通过重写View的onTouchEvent()方法来控制Touch事件的消费与否我们在看57行的ACTION_DOWN,新建一个CheckForTap,我们看看CheckForTap是什么privatefinalclassCheckForTapimplementsRunnable{

publicvoidrun(){

mPrivateFlags&=~PREPRESSED;

mPrivateFlags|=PRESSED;

refreshDrawableState();

if((mViewFlags&LONG_CLICKABLE)==LONG_CLICKABLE){

postCheckForLongClick(ViewConfiguration.getTapTimeout());

}

}

}原来是个Runnable对象,然后使用Handler的post方法延时ViewConfiguration.getTapTimeout()执行CheckForTap的run()方法,在run方法中先判断view是否longClickable的,一般的View都是false,postCheckForLongClick(ViewConfiguration.getTapTimeout())这段代码就是执行长按的逻辑的代码,只有当我们设置为longClickble才会去执行postCheckForLongClick(ViewConfiguration.getTapTimeout()),这里我就不介绍了由于考虑到文章篇幅的问题,我就不继续分析View的长按事件和点击事件了,在这里我直接得出结论吧长按事件是在ACTION_DOWN中执行,点击事件是在ACTION_UP中执行,要想执行长按事件,这个View必须是longclickable的,也许你会纳闷,一般的View不是longClickable为什么也会执行长按事件呢?我们要执行长按事件必须要调用setOnLongClickListener()设置OnLongClickListener接口,我们看看这个方法的源码publicvoidsetOnLongClickListener(OnLongClickListenerl){

if(!isLongClickable()){

setLongClickable(true);

}

mOnLongClickListener=l;

}看到没有,如果这个View不是longClickable的,我们就调用setLongClickable(true)方法设置为longClickable的,所以才会去执行长按方法onLongClick();要想执行点击事件,这个View就必须要消费ACTION_DOWN和ACTION_MOVE事件,并且没有设置OnLongClickListener的情况下,如果设置了OnLongClickListener的情况下,需要onLongClick()返回false才能执行到onClick()方法,也许你又会纳闷,一般的View默认是不消费touch事件的,这不是和你上面说的相违背嘛,我们要向执行点击事件必须要调用setOnClickListener()来设置OnClickListener接口,我们看看这个方法的源码就知道了publicvoidsetOnClickListener(OnClickListenerl){

if(!isClickable()){

setClickable(true);

}

mOnClickListener=l;

}所以说一个enable的并且是clickable的View是一直消费touch事件的,所以才会执行到onClick()方法对于View的Touch事件的分发机制算是告一段落了,从上面我们可以得出TextView的dispatchTouchEvent()的返回false的,即不消费Touch事件。我们就要往上看RelativeLayout的dispatchTouchEvent()方法的51行,由于TextView.dispatchTouchEvent()为false,导致mMotionTarget没有被赋值,还是null,继续往下走执行RelativeLayout的dispatchTouchEvent()方法,来到第84行,判断target是否为null,这个target就是mMotionTarget,满足条件,执行92行的super.dispatchTouchEvent(ev)代码并返回,这里调用的是RelativeLayout父类View的dispatchTouchEvent()方法,由于RelativeLayout没有设置onTouchListener,所以这里直接调用RelativeLayout(其实就是View,因为RelativeLayout没有重写onTouchEvent())的onTouchEvent()方法由于RelativeLayout既不是clickable的也是longClickable的,所以其onTouchEvent()方法false,RelativeLayout的dispatchTouchEvent()也是返回false,这里就执行完了RelativeLayout的dispatchTouchEvent()方法继续执行FrameLayout的dispatchTouchEvent()的第51行,由于RelativeLayout.dispatchTouchEvent()返回的是false,跟上面的逻辑是一样的,也是执行到92行的super.dispatchTouchEvent(ev)代码并返回,然后执行FrameLayout的onTouchEvent()方法,而FrameLayout的onTouchEvent()也是返回false,所以FrameLayout的dispatchTouchEvent()方法返回false,执行完毕FrameLayout的dispatchTouchEvent()方法在上面的我就不分析了,大家自行分析一下,跟上面的逻辑是一样的,我直接画了个图来帮大家理解下(这里没有画出onInterceptTouchEvent()方法)/20140321161531250?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveGlhYW5taW5n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast所以我们点击屏幕上面的TextView的事件分发流程是上图那个样子的,表示Activity的View都不消费ACTION_DOWN事件,所以就不能在触发ACTION_MOVE,ACTION_UP等事件了,具体是为什么?我还不太清楚,毕竟从Activity到TextView这一层是分析不出来的,估计是在底层实现的。但如果将TextView换成Button,流程是不是还是这个样子呢?答案不是,我们来分析分析一下,如果是Button,Button是一个clickable的View,onTouchEvent()返回true,表示他一直消费Touch事件,所以Button的dispatchTouchEvent()方法返回true,回到RelativeLayout的dispatchTouchEvent()方法的51行,满足条件,进入到if方法体,设置mMotionTarget为Button,然后直接返回true,RelativeLayout的dispatchTouchEvent()方法执行完毕,不会调用到RelativeLayout的onTouchEvent()方法然后到FrameLayout的dispatchTouchEvent()方法的51行,由于RelativeLayout.dispatchTouchEvent()返回true,满足条件,进入if方法体,设置mMotionTarget为RelativeLayout,注意下,这里的mMotionTarget跟RelativeLayout的dispatchTouchEvent()方法的mMotionTarget不是同一个哦,因为他们是不同的方法中的,然后返回true同理FrameLayout的dispatchTouchEvent()也是返回true,DecorView的dispatchTouchEvent()方法也返回true,还是画一个流程图(这里没有画出onInterceptTouchEvent()方法)给大家理清下/20140321160329203?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQveGlhYW5taW5n/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast从上面的流程图得出一个结论,Touch事件是从顶层的View一直往下分发到手指按下的最里面的View,如果这个View的onTouchEvent()返回false,即不消费Touch事件,这个Touch事件就会向上找父布局调用其父布局的onTouchEvent()处理,如果这个View返回true,表示消费了Touch事件,就不调用父布局的onTouchEvent()接下来我们用一个自定义的ViewGroup来替换RelativeLayout,自定义ViewGroup代码如下packagecom.example.androidtouchevent;

importandroid.content.Context;

importandroid.util.AttributeSet;

importandroid.view.MotionEvent;

importandroid.widget.RelativeLayout;

publicclassCustomLayoutextendsRelativeLayout{

publicCustomLayout(Contextcontext,AttributeSetattrs){

super(context,attrs,0);

}

publicCustomLayout(Contextcontext,AttributeSetattrs,intdefStyle){

super(context,attrs,defStyle);

}

@Override

publicbooleanonTouchEvent(MotionEventevent){

returnsuper.onTouchEvent(event);

}

@Override

publicbooleanonInterceptTouchEvent(MotionEventev){

returntrue;

}

}我们就重写了onInterceptTouchEvent(),返回true,RelativeLayout默认是返回false,然后再CustomLayout布局中加一个Button,如下图/20140322094019375?waterm

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论