前面关于View绘制的话题好像零散的写过博客,虽然好久没有认真的研究一些东西了,平时忙其他的东西,但是本着每个月必须花几天时间看看android的想法,今天整理了几个View绘制相关的问题,这里不会涉及View测量布局绘制的那部分细节,因为这些前面已经写过了。主要有以下几个问题
1.View绘制流程 invalidate/requestLayout
2.View树和DecorView
4.真的只能在主线程操作UI吗
5.几个函数:WindowManagere#addView、ViewGroup#addView以及PhoneWindow#addContentView的区别
View绘制流程 invalidate/requestLayout
我们平时要刷新界面的时候会主动调用Invalidate函数,其实一些函数也会间接调用invalidate函数比如,setVisibility等与界面有关的函数都会辗转调用到与invalidate相关的一些函数。当调用invalidate函数更新界面重新绘制时,首先要明确,界面的绘制是由ViewRoot开始的。正常情况下,一个Activity就有一个ViewRoot,当某个控件状态改变后调用invalidate函数会一层一层的调用父控件的方法,直到调用的View树的根,也即ViewRoot。最后ViewRoot会调用scheduleTraversals函数,里面会产生一个异步的绘制事件执行我们都知道的控件绘制相关的三个函数measure、layout和draw函数,简单地说就是某个View要重新绘制时,都是请求它所在的View树根来绘制的。
函数调用流程如图所示,当某个View调用到invalidate函数,它会调用父View(准确的说应该是ViewParent对象)的invalidateChild函数,在调用到invalidateChildInParent函数,在这个函数里面会循环调用上一层父View的invalidateChildInParent函数,这部分所做的处理主要是设置View的mPrivateFlags参数的位标志,计算要绘制的矩形等,直到调用到View树的根。
当某个View就是View树的第一个孩子,它调用invalidate重新绘制时,就直接调用到ViewRoot的invalidateChild函数,也就是图中虚线部分,在ViewRoot中的invalidateChild函数中也仅仅调用了invalidateChildInParent函数。这个View一般来说对应的就是DecorView。
同样还有一个用来绘制的函数requestLayout,这个函数也会导致View树的重新测量、布局,这个函数要比invalidate函数更强一点,前面说的invalidate函数主要作用于draw函数,也就是强制重新绘制当前View,当要绘制的View的大小位置没有变化时是就可以不重新测量和layout布局了,只需要绘制即可,而当使用requestLayout函数时,就会强制测量和重新布局,其实也就是设置了View的mPrivateFlags的某些位的标记。
public void requestLayout() {
mPrivateFlags |= PFLAG_FORCE_LAYOUT;
mPrivateFlags |= PFLAG_INVALIDATED;
if (mParent != null && !mParent.isLayoutRequested()) {
mParent.requestLayout();
}
}
当调用到measure时由于PFLAG_FORCE_LAYOUT已经被设置,所以就会重新measure,在layout时也是类似的。
public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
if ((mPrivateFlags & PFLAG_FORCE_LAYOUT) == PFLAG_FORCE_LAYOUT ||
widthMeasureSpec != mOldWidthMeasureSpec ||