歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
您现在的位置: Linux教程網 >> UnixLinux >  >> Linux編程 >> Linux編程

Android View系統中無效區管理的bug

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

Copyright © Linux教程網 All Rights Reserved