今天碰到这么一个问题,我有一个activity,布局如下:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@drawable/page_bg" >
<com.ebensz.widget.SVGEditor android:id="@+id/svg_editor"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
其中page_bg是一张jpg图片,在调试svg_editor的绘制速度的时候,发现有三处主要的绘制,分别是ColorDrawable、BitmapDrawable和svg_editor,后面两个自然是RelativeLayout和我自定义的控件,ColorDrawable就很奇怪了,我没有一处使用,只能是窗口饰件(decoView)了,这点很快就得到了确认。但我的RelativeLayout使用JPG图片做背景,而JPG图片是不可能有透明色的,再画RelativeLayout的父视图不是画蛇添足么?
我首先把怀疑的目光集中到了RelativeLayout上,是不是因为我没有指明它不透明,系统把它当透明的了呢,于是查看了VIEW类的isOpaque和computeOpaqueFlags函数,它是根据背景是否透明设定视图的透明属性的,问题很快被澄清了,RelativeLayout确实是不透明的。那问题出在哪呢?
于是我又把View的invalidate和绘制流程梳理了一遍。invalidate的流程很简单,先是设置无效标记,然后调用parent view的invalidateChild,它的主要代码如下:
// Check whether the child that requests the invalidate is fully opaque
final boolean isOpaque = child.isOpaque() && !drawAnimation &&
child.getAnimation() != null;
// Mark the child as dirty, using the appropriate flag
// Make sure we do not set both flags at the same time
final int opaqueFlag = isOpaque ? DIRTY_OPAQUE : DIRTY;
do {
View view = null;
if (parent instanceof View) {
view = (View) parent;
}
if (drawAnimation) {
if (view != null) {
view.mPrivateFlags |= DRAW_ANIMATION;
} else if (parent instanceof ViewRoot) {
((ViewRoot) parent).mIsAnimating = true;
}
}
// If the parent is dirty opaque or not dirty, mark it dirty with the opaque
// flag coming from the child that initiated the invalidate
if (view != null && (view.mPrivateFlags & DIRTY_MASK) != DIRTY) {
view.mPrivateFlags = (view.mPrivateFlags & ~DIRTY_MASK) | opaqueFlag;
}
parent = parent.invalidateChildInParent(location, dirty);
} while (parent != null);
invalidateChildInParent只是简单的向上遍历,最终把无效区交给rootView引起窗口重绘,关键是DIRTY_OPAQUE 和DIRTY的意思。查看一下绘制流程,当DIRTY_OPAQUE 标记为真时,跳过了本view的绘制,直接绘制childview,可见它表示非透明的子窗口无效引起的绘制,这种情况下parent view是不需要绘制的。上述代码也体现了这种思想,但当引起绘制的view本身是透明的,而它的某个parent view非透明时,上述代码并没有继续检查,而是要求所有parent view重新绘制,问题的根源就在这里。
究竟是google的一个bug,还是考虑到某种需要,又或者有某种途径能解决这个问题?问题尚未解决,同志仍需努力。
(跟问题花了不到一个小时,写文章倒是花了半个小时以上,汗:)