起因
上篇对View的Measure流程进行了大志的了解,于是动手写一个自定义View来测试下自己的总结。最终由一个onSizeChanged()方法引发了一场头脑风暴。
当我们在activity的onCreate()方法中调用setContentView()来加载我们预先编写好的布局文件时:
问题
- 什么时候调用onFinishInflate(),onSizeChanged()方法
- 为什么同一个view在绘制流程中,调用了两次onMeasure()和onLayout()
过程
onFinishInflate(),onSizeChanged()
onFinishInflate()是从XML(布局文件)加载View后回调的
onSizeChanged()是在View大小改变时回调的
那么,onSizeChanged()判断View大小改变的依据是什么,也就是View初始值是多少呢?从上面的Log日志可以看出,View的长宽初始值都是0px。所以当我们在布局文件中修改Layout_width/height时,就可能调用onSizeChanged()方法。
针对第二个问题,为什么会调用两边onMeasure()和onLayout()方法
Google到一张图片给以启发:
可能是调用了requestLayout()方法重新遍历了一遍View Tree。那么requestLayout()方法是用来干嘛的呢?
当布局变化的时候,比如方向变化,尺寸的变化,会调用该方法,在自定义的视图中,如果某些情况下希望重新测量尺寸大小,应该手动去调用该方法,它会触发measure()和layout()过程,但不会进行 draw。只是对View树重新布局layout过程包括measure()和layout()过程。不会调用draw()过程,不会重新绘制任何视图包括该调用者本身。
由此我联想到了View在从XML文件加载后并遍历第一遍时并不是可视的。当执行activity的onResume()变成可视状态(即View的状态发生改变,调用requestLayout()),导致重新遍历,就有了第二次的onMeasure()和onLayout()
现在回想起来自己也是鱼,requestLayout()解释的那么清楚(当view确定自身已经不再适合现有的区域时,该view本身调用这个方法要求parent view重新调用他的onMeasure onLayout来对重新设置自己位置。)不会调用onDraw(),而且在布局文件中已经设置为可视,更不会有可视状态的改变。还会想的这么扯淡!
但是看上面的Log,打印出View的可视属性时,已经是可见的,想法不成立。
接着继续Google,发现一篇博客中提到一点
在View的Measure、Layout、Draw三个流程中,最终会直接或间接调用到三个函数,分别为invalidate(),requsetLaytout()以及requestFocus() ,接着这三个函数最终会调用到ViewRoot中的schedulTraversale()方法,该函数然后发起一个异步消息,消息处理中调用performTraverser()方法对整个View进行遍历。
打印出Log显示,果然是这样的!
参考文献:文献1,文献2,文献3
目标:了解Android窗口机制