碰到這麼一個問題,我有一個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,還是考慮到某種需要,又或者有某種途徑能解決這個問題?問題尚未解決,同志仍需努力。