View的生命周期函数执行顺序:
写一个类继承View,重写生命周期的各个回调函数,并且打印Log,以onDraw()函数为例这样打Log:
输出的Log如下:
V: construct w/h: 0 0 mW/mH: 0 0
V: onFinishInflate w/h: 0 0 mW/mH: 0 0
V: onAttachedToWindow w/h: 0 0 mW/mH: 0 0
V: onWindowVisibilityChanged w/h: 0 0 mW/mH: 0 0
V: onVisibilityChanged w/h: 0 0 mW/mH: 0 0
V: onMeasure w/h: 0 0 mW/mH: 1080 2302
V: onMeasure w/h: 0 0 mW/mH: 1080 2400
V: onSizeChanged w/h: 1080 2400 mW/mH: 1080 2400
V: onLayout w/h: 1080 2400 mW/mH: 1080 2400
V: onDraw w/h: 1080 2400 mW/mH: 1080 2400
V: onDrawForeground w/h: 1080 2400 mW/mH: 1080 2400
V: onWindowFocusChanged w/h: 1080 2400 mW/mH: 1080 2400
View在初始化时onMeasure()函数执行了两次,这个函数里面用getWidth()和getHeight()是拿不到宽高的,getMeasureWidth()和getMeasureHeight()可以拿到宽高,第一次拿到的宽高比第二次少98像数,看着这98像是通知栏的高度(此时的主题是隐藏通知栏的, 如下)
<item name="android:windowFullscreen">true</item> //满屏
于是去主题文件thems.xml把满屏的属性设置成false,这样onMeasure方法两次拿到的宽高是一样的,具体原因还得去挖源码
为什么getMeasureXXX()可以拿到宽高?看看源码里面的getMeasureWidth()方法:
mMeasureWidth是在setMeasuredDimension()去设置的,重写onMeasuer()方法的目的之一就是调用setMeasuredDimension()方法去设置这个值
这里mMeasureWidth 和 MEASURED_SIZE_MASK 做了一个 &(按位与,也叫逻辑与)运算,MEASURED_SIZE_MASK是一个16进制数,他们在做运算时都会转成2进制数,可以看到它的低位都是f,那转成二进制数的时候低位全是1,在做&运算时可以起到限制mMeasureWidth大小的作用,如图:
---------------------------------------------------------------------------------------------------------------------------------
回归正题,从Log可以看出第一次能拿到view的宽高应是在onSizeChange()里面,但是在源码里头onSizeChange()这个函数没有写东西,它是由SizeChange()去调用的,调用sizeChange()的有四个函数setTop()、setLeft()、setRight()、setFrame()。 而 Layout()调用了setLFrame(),setFrame()函数在调用sizeChange()前设置了mLeft 和 mRight等属性,而getWidth()方法是返回View里的 (mRight-mLeft),所以在onSizeChange()函数里可以拿到宽高属性
View.java里setFrame()方法的代码片段:
在重写的onSizeChange()函数里打个断点再看看调用栈:
从这里看View宽高的初始化应该是在onSizeChange()里(或者说是在layout()里)
---------------------------------------------------------------------------------------------------------------------------------
把屏幕旋转后Log输出的内容:(跟上面view的初始化相比,多了红色的内容,多了onWindowVIsibility()和onDetachedFromWndow()这两个函数)
V: onWindowVisibilityChanged w/h: 2400 1080 mW/mH: 2400 1080
V: onDetachedFromWindow w/h: 2400 1080 mW/mH: 2400 1080
V: construct w/h: 0 0 mW/mH: 0 0
V: onFinishInflate w/h: 0 0 mW/mH: 0 0
V: onAttachedToWindow w/h: 0 0 mW/mH: 0 0
V: onWindowVisibilityChanged w/h: 0 0 mW/mH: 0 0
V: onVisibilityChanged w/h: 0 0 mW/mH: 0 0
V: onMeasure w/h: 0 0 mW/mH: 1080 2400
V: onMeasure w/h: 0 0 mW/mH: 1080 2400
V: onSizeChanged w/h: 1080 2400 mW/mH: 1080 2400
V: onLayout w/h: 1080 2400 mW/mH: 1080 2400
V: onDraw w/h: 1080 2400 mW/mH: 1080 2400
V: onDrawForeground w/h: 1080 2400 mW/mH: 1080 2400
V: onWindowFocusChanged w/h: 1080 2400 mW/mH: 1080 2400
从Log看到view的整个生命周期函数都执行了一遍,因为转屏时Activity重建了,想要转屏时Activity不重建,在manifest文件对应的Activity加上这个配置:
这样的话执行的生命周期函数如下:
V: onMeasure w/h: 1080 2400 mW/mH: 2400 1080
V: onSizeChanged w/h: 2400 1080 mW/mH: 2400 1080
V: onLayout w/h: 2400 1080 mW/mH: 2400 1080
V: onDraw w/h: 2400 1080 mW/mH: 2400 1080
V: onDrawForeground w/h: 2400 1080 mW/mH: 2400 1080
可以看到onMeasure函数执行了一次,但是多转几次屏次次,有时候onMeasure函数会执行3次,具体原因没有去考究