Android源码学习之ViewGroup

2015-02-02

Android源码学习之ViewGroup

android继承于View,同时它包含了一个View数组存储它的子View,也就是说它是一个容器。这就是经典的设计模式中的组合模式。在Android的视图结构中,容器一定是ViewGroup,只有ViewGroup才能包含其他视图,像TextView,ImageView这些View是不能包含子视图的,它们是单一控件。这一点跟iOS的视图结构很不一样的。在iOS中所有的视图都是继承于UIView,同时UIView也是一个容器,能包含其他UIView。从这一点来说iOS的视图结构更简单。

是什么使得ViewGroup具有了容易功能呢?ViewGroup除了继承View外,还是实现了两个接口:ViewManagerViewParent。ViewManager主要定义了addView和removeView的方法,ViewParent主要定义了刷新容器的接口requestLayout和其他一些焦点事件的处理等接口。除此之外,还有非常重要的一点,ViewGroup是一个抽象类。我们知道View并不是抽象类,为什么ViewGroup继承了View反而成了抽象类呢,是增加了什么新的抽象方法吗?不是,ViewGroup中的抽象方法是继承于View的onLayout方法。onLayout方法在View中并不是抽象方法,只是一个空方法,但是在ViewGroup中它被定义为了抽象方法。我第一次发现原来还可以这样做。onLayout的主要作用是放置子View的位置,而不同的布局方式算法不一样,所有留给子类实现比较合理。

ViewGroup中并没有覆盖onMeasure方法,但是增加了一些计算子View大小的方法:measureChildren方法就是将所有的子View都遍历一遍,并调用他们的measure方法。ViewGroup中同样没有覆盖onDraw方法,但是覆盖了dispatchDraw方法。dispatchDraw方法主要作用是调用每个子View的draw方法。看来onXXX方法在ViewGroup中并不重要,它更多的在调用子View的方法。

LayoutParams

布局参数LayoutParams是ViewGroup的一个重要内部类。我们在做布局时必不可少的两个属性就是这个类的属性:width和height。程序运行时任何一个控件都有这两个值,当然就是必不可少。LayoutParams这个类非常简单,处理width和height,还有一个LayoutAnimationController.AnimationParameters 属性,用于布局动画的。几乎所有的ViewGroup类的子类都有自己的内部类LayoutParam,但是他们都不是直接继承于LayoutParams,而是MarginLayoutParams,MarginLayoutParams同样是ViewGroup的内部类。MarginLayoutParams无非就是加入了各个方向的margin,这个很好理解,我们布局时都是带margin的。初学者开始搞不懂margin和padding的区别。其实只要记住margin是不属于View的空间的,它是用于每个View与父View或者兄弟View之前的间距,而padding是属于View内部空间的,是View自己的内容跟View边框之间的间距。从代码角度来看,padding是View的属性,LayoutParams是ViewGroup的属性。

dispatchTouchEvent触摸事件的分发

基本上我每次面试别人,事件的分发是我必问的问题。这个其实没什么难的,网上很多资料说这个,但是看没看过源代码的人看他的回答基本就能知道。Android的触摸事件分发是自上而下的,是通过dispatchTouchEvent来分发的。ViewGroup的dispatchTouchEvent方法还是挺复杂的。ViewGroup在分发事件给子View前,会先调用自身的onInterceptTouchEvent方法来判断自己是否要拦截掉事件,如果拦截了则不会再分发给子View了。没有拦截的话就会在自己的子View中寻找事件接收者,并且组成一个TouchTarget链表,将事件传给他们处理(dispatchTransformedTouchEvent)。简而言之,ViewGroup在分发触摸事件之前会先看自己是否要拦截该事件,不拦截的话在子View中寻找没有隐藏并且在触摸点范围内的子View,将能处理这个事件的加入TouchTarget链表,分发事件给他们处理。我一般面试会问触摸事件会涉及哪些方法,每个方法大概做了什么,哪些是ViewGroup的方法,哪些是View的方法。只要能答出有dispatchTouchEvent, onInterceptTouchEvent,onTouchEvent方法,onInterceptTouchEvent是ViewGroup独有的方法,其他两个是View的方法 就过关。能说明如果一个View设置了OnTouchEventListener,就先调用OnTouchEventListener的回调更好。

自定义ViewGroup

自定义ViewGroup的需求比较少。一般将已有的ViewGroup子类做组合基本足够。不过真要自定义的话,至少知道该怎么做。必须重写的方法是onLayout方法。这个方法就是对子View布局,即放在哪里。还有一个一般都要重写的方法是onMeasure方法。View的onMeasure比较简单,但是要布局类的话,需要自上而下来计算子View的大小,然后才能在onLayout中根据子View的大小放置。还有可能要重写的是dispatchDraw和drawChild方法。当然跟根据需求可能对dispatchTouchEvent重写。

关于ViewGroup就先写到这吧。

Category: Android Tagged: Android源码学习 ViewGroup

Comments