安卓View开发心得(二)

2.善于利用“生命周期”

安卓VIew生命周期是面向对象设计模式中“模板方法模式”的一个经典应用。由系统服务(AMS、WMS等)维护View的生命周期,这些View的生命周期的细节app开发者可以不必了解,只需要知道一些关键的逻辑节点;这些关键的逻辑节点以关键的类似onXXX()方法的方式存在,app开发者只要在自定义的View子类中重写这些方法即可。一个视图(View),从内存中的对象被创建,到最终在屏幕上绘制渲染被用户看到,中间经历和很漫长、复杂的过程。所以,使用生命周期的好处在于,第一开发者可以免去学习View技术细节的成本,第二如果开发者手动去实现,可能会与复杂的View逻辑产生冲突,从而不能达到自己代码的目的,陷入各种变态问题中。对于View,有一些关键的生命周期方法。可谓Android给开发者的福利。

众所周知,安卓的View是可以定义到XML文件中的。从XML文件中的一段文本,到手机屏幕上可以看到的视图,从View的角度,经历四个过程:inflate(从XML文件加载到内存中的一个View对象)、measure(计算大小)、layout(确定位置)、draw(绘制)。下面按顺序聊聊:

onFinishInflate():Called after a view and all of its children has been inflated from XML.
Inflate View的概念是,将XML文件中定义的一个View,实例化到内存的一个View对象。需要注意:第一,不管有没有手动调用LayoutInflater.inflate(),View的这个回调方法都是会被调用的,这是View生命周期的开始;第二,将一个View从XML实例化到内存中的一个对象,也仅仅是内存对象而已,距离被看到,还很遥远。

onMeasure(int,int):Measure the view and its content to determine the measured width and the measured height. This method is invoked by measure(int, int) and should be overridden by subclasses to provide accurate and efficient measurement of their contents.
measure():This is called to find out how big a view should be. The parent supplies constraint information in the width and height parameters.
The actual measurement work of a view is performed in onMeasure(int, int), called by this method. Therefore, only onMeasure(int, int) can and must be overridden by subclasses.
measure的概念是根据parent view传入的约束性参数width和height,计算确定View的width和height。View中比较常用的api getMeasuredWidth()/Height()即得到最近一次measure之后的View的width和height。因为View有生命周期,并非一出生就能够得到正确的width和height。但经过measure以后,width和height已经有效。下面详细分析一下这个过程。
对于Parent View和Child View,这个measure的逻辑是怎么样的?先看看View这个所有视图类的父类的measure()和onMeasure():
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
......
}
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}
可以看到两个关键信息:第一,measure()是final的,也就是说,所有视图类都不可以改变measure的逻辑,只能在重写onMeasure上面做文章;第二,View类的onMeasure非常简单,只是调用setMeasuredDimension来配置一下默认的measuredWidth/Height(也就是getMeasuredWidth()/Height()拿到的值)。再以常用的TextView和RelativeLayout为例,看看它的onMeasure。
TextView的onMeasure()相对复杂,170行左右,在最后调用了 setMeasuredDimension(),并且没有调用super.onMeasure()。
RelativeLayout继承自ViewGroup,适合通过其了解parent View和child View在measure过程中的逻辑。GroupView自己没有重写onMeasure,直接继承自View.onMeasure()。RelativeLayout的onMeasure 250行左右,同样是在最后调用了setMeasuredDimension(),没有调用super.onMeasure(),最关键的是,调用了child View的measure:
for (int i = 0; i < count; i++) {
    final View child = views[i];
    if (child.getVisibility() != GONE) {
        ......
        measureChild(child, params, myWidth, myHeight);
        ......
}
measureChild()中有如下代码:
private void measureChild(View child, LayoutParams params, int myWidth, int myHeight) {
    int childWidthMeasureSpec = getChildMeasureSpec(params.mLeft,
            params.mRight, params.width,
            params.leftMargin, params.rightMargin,
            mPaddingLeft, mPaddingRight,
            myWidth);
    int childHeightMeasureSpec = getChildMeasureSpec(params.mTop,
            params.mBottom, params.height,
            params.topMargin, params.bottomMargin,
            mPaddingTop, mPaddingBottom,
            myHeight);
    child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
}
所以,对measure过程,Parent View和Child View是一个迭代过程,Parent View进入measure,启动Child View的measure,等到所有的Child measure完成,再根据child的数据完成自己的measure。
总结下来,关于在自己开发的View中使用onMeasure()需要注意:
(1)或者调用super.onMeasure,或者根据自己的逻辑手动去调用setMeasuredDimension();
(2)如果是自己开发ViewGroup,需要保证能够触发child view的measure。如果是继承自安卓提供的成熟ViewGroup框架类,譬如RelativeLayout,那可以通过调用super.onMeasure或者手动调用child view的measure()两种方法实现;如果直接继承自ViewGroup,就只能手动调用child view的measure()了。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值