1. 程式人生 > >decorView和window之間的層級及關係

decorView和window之間的層級及關係

首先貼出實現Activity對話方塊圓角的核心程式碼

@Override
    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        DisplayMetrics dm = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(dm);
        View view = getWindow().getDecorView();
        WindowManager.LayoutParams lp = (WindowManager.LayoutParams)view.getLayoutParams();
        lp.gravity = Gravity.CENTER;
        lp.width = (dm.widthPixels * 4) / 5;
        lp.height = (dm.widthPixels * 4) / 5;
        getWindowManager().updateViewLayout(view,lp);
        getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        view.setBackgroundResource(R.drawable.dialog_activity_bg);
    }
在上篇部落格onAttachedToWindow()在整個Activity生命週期的位置及使用中解釋了為什麼在onAttachedToWindow中修改視窗尺寸,上面程式碼最後兩行分別對window和decorView設定背景,那麼問題來了,一個窗體中decorView和window之間的關係是什麼?

通過文章開始貼出的程式碼View view = getWindow().getDecorView();就可以對Window和DecorView的層級關鍵猜測一二,decorView是否作為一個變數由window維護?
和之前思路一樣,想探究這個問題就得看原始碼說話。這裡依然參考老羅的部落格
Android應用程式視窗(Activity)的檢視物件(View)的建立過程分析

既然猜測decorView是window的變數,那麼就先找到window和activity之間的關係。通過原始碼發現

public class Activity extends ContextThemeWrapper  
        implements LayoutInflater.Factory,  
        Window.Callback, KeyEvent.Callback,  
        OnCreateContextMenuListener, ComponentCallbacks {  
    ......  
  
    private Window mWindow;  
    ......  
  
    public Window getWindow() {  
        return mWindow;  
    }  
    ......  
  
    public void setContentView(int layoutResID) {  
        getWindow().setContentView(layoutResID);  
    }  
  
    ......  
}  
Window原來是Activity的一個變數,可以通過getWindow()獲取,而且Activity中經常用到的setContentView原來呼叫的是window的setContentView。那麼Window是什麼?在Activity中起什麼作用?OK~帶著問題再查閱Google的官方文件。

Abstract base class for a top-level window look and behavior policy. An instance of this class should be used as the top-level view added to the window manager. It provides standard UI policies such as a background, title area, default key processing, etc.

The only existing implementation of this abstract class is android.policy.PhoneWindow, which you should instantiate when needing a Window. Eventually that class will be refactored and a factory method added for creating Window instances without knowing about a particular implementation.

Google說Window是WindowManager最頂層的檢視,它負責背景(視窗背景)、Title之類的標準的UI元素,Window是一個抽象類,整個Android系統中PhoneWindow是Winodw的唯一實現類。所以接下來進入PhoneWinodw一探究竟。
public class PhoneWindow extends Window implements MenuBuilder.Callback {  
    ......  
  
    // This is the top-level view of the window, containing the window decor.  
    private DecorView mDecor;  
    ......  
  
    // This is the view in which the window contents are placed. It is either  
    // mDecor itself, or a child of mDecor where the contents go.  
    private ViewGroup mContentParent;  
    ......  
  
    private TextView mTitleView;  
    ......  
  
    private CharSequence mTitle = null;  
    ......  
  
    private void installDecor() {  
        if (mDecor == null) {  
            mDecor = generateDecor();  
            ......  
        }  
        if (mContentParent == null) {  
            mContentParent = generateLayout(mDecor);  
  
            mTitleView = (TextView)findViewById(com.android.internal.R.id.title);  
            if (mTitleView != null) {  
                if ((getLocalFeatures() & (1 << FEATURE_NO_TITLE)) != 0) {  
                    View titleContainer = findViewById(com.android.internal.R.id.title_container);  
                    if (titleContainer != null) {  
                        titleContainer.setVisibility(View.GONE);  
                    } else {  
                        mTitleView.setVisibility(View.GONE);  
                    }  
                    if (mContentParent instanceof FrameLayout) {  
                        ((FrameLayout)mContentParent).setForeground(null);  
                    }  
                } else {  
                    mTitleView.setText(mTitle);  
                }  
            }  
        }  
    }  
  
    ......  
}  
在這裡總算找到了DecorView,和上面猜測的一樣,DecorView確實為Window的變數。同時還發現一個名為mContentParent的ViewGroup,那麼這個變數的作用是什麼?和DecorView有什麼關係?帶著問題接著往下看。

PhoneWindow的setContentView方法

@Override
    public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        mLayoutInflater.inflate(layoutResID, mContentParent);
        final Callback cb = getCallback();
        if (cb != null && !isDestroyed()) {
            cb.onContentChanged();
        }
    }
在mContentParent為null時會呼叫installDecor()來建立應用程式視窗檢視物件。接著在installDecor()中呼叫generateLayout為mContentParent賦值。

    protected ViewGroup generateLayout(DecorView decor) {
        // 獲取<Activity android:theme=""/>中的theme屬性或者程式碼requestWindowFeature()中指定的Features

        TypedArray a = getWindowStyle();

        if (false) {
            System.out.println("From style:");
            String s = "Attrs:";
            for (int i = 0; i < com.android.internal.R.styleable.Window.length; i++) {
                s = s + " " + Integer.toHexString(com.android.internal.R.styleable.Window[i]) + "="
                        + a.getString(i);
            }
            System.out.println(s);
        }

        mIsFloating = a.getBoolean(com.android.internal.R.styleable.Window_windowIsFloating, false);
        int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
                & (~getForcedWindowFlags());
        if (mIsFloating) {
            setLayout(WRAP_CONTENT, WRAP_CONTENT);
            setFlags(0, flagsToUpdate);
        } else {
            setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
        }

        //...


        // Inflate the window decor.

        int layoutResource;
        int features = getLocalFeatures();
        // System.out.println("Features: 0x" + Integer.toHexString(features));
        if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        com.android.internal.R.attr.dialogTitleIconsDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = com.android.internal.R.layout.screen_title_icons;
            }
            // XXX Remove this once action bar supports these features.
            removeFeature(FEATURE_ACTION_BAR);
            // System.out.println("Title Icons!");
        } else if ((features & ((1 << FEATURE_PROGRESS) | (1 << FEATURE_INDETERMINATE_PROGRESS))) != 0
                && (features & (1 << FEATURE_ACTION_BAR)) == 0) {
            // Special case for a window with only a progress bar (and title).
            // XXX Need to have a no-title version of embedded windows.
            layoutResource = com.android.internal.R.layout.screen_progress;
            // System.out.println("Progress!");
        } else if ((features & (1 << FEATURE_CUSTOM_TITLE)) != 0) {
            // Special case for a window with a custom title.
            // If the window is floating, we need a dialog layout
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        com.android.internal.R.attr.dialogCustomTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = com.android.internal.R.layout.screen_custom_title;
            }
            // XXX Remove this once action bar supports these features.
            removeFeature(FEATURE_ACTION_BAR);
        } else if ((features & (1 << FEATURE_NO_TITLE)) == 0) {
            // If no other features and not embedded, only need a title.
            // If the window is floating, we need a dialog layout
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        com.android.internal.R.attr.dialogTitleDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else if ((features & (1 << FEATURE_ACTION_BAR)) != 0) {
                layoutResource = com.android.internal.R.layout.screen_action_bar;
            } else {
                layoutResource = com.android.internal.R.layout.screen_title;
            }
            // System.out.println("Title!");
        } else if ((features & (1 << FEATURE_ACTION_MODE_OVERLAY)) != 0) {
            layoutResource = com.android.internal.R.layout.screen_simple_overlay_action_mode;
        } else {
            // Embedded, so no decoration is needed.
            layoutResource = com.android.internal.R.layout.screen_simple;
            // System.out.println("Simple!");
        }

        mDecor.startChanging();

        View in = mLayoutInflater.inflate(layoutResource, null);
        decor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));

        ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
        if (contentParent == null) {
            throw new RuntimeException("Window couldn't find content container view");
        }

        if ((features & (1 << FEATURE_INDETERMINATE_PROGRESS)) != 0) {
            ProgressBar progress = getCircularProgressBar(false);
            if (progress != null) {
                progress.setIndeterminate(true);
            }
        }

        // Remaining setup -- of background and title -- that only applies
        // to top-level windows.
        if (getContainer() == null) {
            Drawable drawable = mBackgroundDrawable;
            if (mBackgroundResource != 0) {
                drawable = getContext().getResources().getDrawable(mBackgroundResource);
            }
            mDecor.setWindowBackground(drawable);
            drawable = null;
            if (mFrameResource != 0) {
                drawable = getContext().getResources().getDrawable(mFrameResource);
            }
            mDecor.setWindowFrame(drawable);

            // System.out.println("Text=" + Integer.toHexString(mTextColor) +
            // " Sel=" + Integer.toHexString(mTextSelectedColor) +
            // " Title=" + Integer.toHexString(mTitleColor));

            if (mTitleColor == 0) {
                mTitleColor = mTextColor;
            }

            if (mTitle != null) {
                setTitle(mTitle);
            }
            setTitleColor(mTitleColor);
        }

        mDecor.finishChanging();

        return contentParent;
    }
上面程式碼可看到將一些頁面特性佈局,例如ActionBar、Title等新增到decorView中,並且根據程式碼ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);可看到原來mContentParent是id="@android:id/content"的ViewGroup。再回到PhoneWindow.setContentView
mLayoutInflater.inflate(layoutResID, mContentParent);
原來我們自定義的View都是mContentParent的子View。並且上面這段程式碼還發現了為什麼必須在setContentView之前才能執行requestWindowFeature的彩蛋。

根據上面所有的猜測和分析,最終發現Window、DecorView和mContentParent的層級關係如下圖(如有誤請指正)


參考資料:

相關推薦

decorViewwindow之間層級關係

首先貼出實現Activity對話方塊圓角的核心程式碼 @Override public void onAttachedToWindow() { super.onAttachedToWindow(); DisplayMetrics

關於Toolbar中navigationIcontitle之間距離展開

boolean ionic code sin block max change nav 希望 關於Toolbar中navigationIcon和title之間距離及展開 問題緣起 在進行Coolcode項目的MyclassActivity中,我發現navigationI

乾貨丨什麼是API,SDKAPI之間有什麼關係呢?

隨著軟體規模的日益龐大,常常需要把複雜的系統劃分成小的組成部分,程式設計介面的設計十分重要,程式設計的實踐中,程式設計介面的設計首先要使軟體系統的職責得到合理劃分,良好的介面設計可以降低系統各部分的相互依賴,提高組成單元的內聚性,降低組成單元間的耦合程度,從而提高系統的維護性和擴充套件性。API

關於javascript閉包(Closure)return之間的曖昧關係

  什麼是閉包?阮一峰老師說的很清楚了,定義在一個函式內部的函式,在本質上,閉包就是將函式內部和函式外部連線起來的一座橋樑。   首先要了解Javascript的變數作用域:全域性變數和區域性變數。全域性嘛,就是共享,任何一個函式內部可以直接讀取全域性變數;區域性嘛,就是私有,不暴露在外的。如何判斷該變數是

關於event window.event問題瀏覽器相容問題

< html> < script language=“javascript”> function test(event) { event = event || window.event; if(event.keyCod

類/物件物件之間的三大關係

線段上有兩個點 - has-a - 關聯關係 整體不可分割的,叫做強關聯/聚合 eg:人有手,車有引擎 人使用了房子 - use-a - 使用,依賴關係 學生是人 - is-a - 繼承關係 繼承 - 從已經有的類建立新類的過程 提供繼承資訊的稱為父類(超類/基

【Linux】UnixLinux的歷史關係

Unix和Linux的關係 悉悉嗦嗦 Unix的歷史 Linux簡介 Mac OS 悉悉嗦嗦 對於接觸計算機不久的同學來說,可能經常聽到類似:Unix,Linux,類Unix

Solidify實現一個智慧合約9(陣列string之間的轉換關係

固定大小位元組陣列之間的轉換 固定大小位元組陣列,我們可以通過bytes1~32來進行宣告,固定大小位元組陣列的長度不可變,內容不可修改。 pragma solidity ^0.4.4; contr

陣列連結串列與棧佇列之間關係之間關係

本屌最近在學習資料結構過程中,由於連續看了陣列,棧,佇列,連結串列等,一時混雜,下面摘取參考資料,供自己學習使用。 第一部分:介紹了資料儲存結構和資料結構的區別,以及連結串列和陣列的差異。 第二部分:介紹了堆和棧的區別。 (1)資料儲存結構:計算機的一個概念,描述資料在計算機中儲存方式;常用

C++_派生類的建構函式派生類基類之間的特殊關係

派生類和基類的概念及派生類建構函式的原理: 建立一個叫做TableTennisPlayer的基類,記錄會員的名字和是否有球桌。 1 //宣告一個基類 2 class TableTennisPlayer 3 { 4 private: 5 string firstname; 6

activityview之間關係。Android Activity 、 Window 、 View之間關係

https://blog.csdn.net/haiyang497661292/article/details/78097775 一、簡述如何將Activity展現在手機上 Tips: Activity本身是沒辦法處理顯示什麼控制元件(view)的,是通過Phone

Java中MapSet之間關係Map.Entry)

1、通過查詢API文件: 2、Map.Entry是一個介面,所以不能直接例項化。 3、Map.entrySet( )返回的是一個collection集合,並且,這個collection中的元素是Map.Entry型別,如下圖所示: 4、 Map是Java中的

理清Activity、ViewWindow之間關係

View、Window以及Activity主要是用於顯示並與使用者互動的。這讓我們在初學的時候很容易弄混,而且無法理解他們區別以及聯絡。本文是筆者查閱相關資料後,結合自己的理解寫出來。希望能幫你梳理清楚他們各自的工作職責,以及是因為什麼需求導致了它們的出

JDBC中PreparedStatement接口提供的execute、executeQueryexecuteUpdate之間的區別用法

ica cat nvi 一個 execute ear let ace 刪除 JDBC中PreparedStatement接口提供的execute、executeQuery和executeUpdate之間的區別及用法 (2012-08-27 09:36:18) 轉載▼

【轉】rhel核心版本HBA卡驅動版本之間的對應關係

https://access.redhat.com/solutions/2109211 Red Hat Enterprise Linux Kernel releases and corresponding HBA driver versions  SOLUTION 已驗證 

Java中JDK,JREJVM之間關係-(轉載)

初學JAVA很容易被其中的很多概念弄的傻傻分不清楚,首先從概念上理解一下吧,JDK(Java Development Kit)簡單理解就是Java開發工具包,JRE(Java Runtime Enviroment)是Java的執行環境,JVM( java virtual machine)也就是

主表附表的關聯關係,普通欄位就可以實現為什麼還要有主鍵外來鍵?之間有什麼關係

主鍵和外來鍵是把多個表組織為一個有效的關係資料庫的粘合劑。主鍵和外來鍵的設計對物理資料庫的效能和可用性都有著決定性的影響。必須將資料庫模式從理論上的邏輯設計轉換為實際的物理設計。而主鍵和外來鍵的結構是這個設計過程的癥結所在。一旦將所設計的資料庫用於了生產環境,就很難對這些鍵進行修改,所以在開發階段就

淺談頻寬、網速流量之間關係

通常情況下:我們說的頻寬10M  20M ;現在網速:200KB/s ; 使用了8M的流量等,那麼頻寬、網速、流量之間有什麼關係,他們分別代表什麼呢? ①頻寬的統計單位是:位元/秒(bps):10M=10Mbps; ②網速是資料傳輸的速度,單位是:位元組/秒(B/s KB/s MB/

關於作業系統中程序、執行緒、任務之間關係

  Vxworks系統中程序、執行緒和任務之間的關係             

好虐!一張圖看懂程式設計師測試之間關係

在開始我們今天的話題之前,我們先看一張圖: 微博網友評論: 小A:最近一直在被測試按在地上反覆摩擦 小B:笑死!!不夠專業的測試也是浪費超級多溝通時間呀!! 小C:最後都是傑瑞贏了 小D:真的是挺悲哀的 小E:也有可能是產品和程式設計師 從事軟體測試行業幾年,合作的