View是如何添加到Activity中

轉(zhuǎn)自:http://blog.csdn.net/feiduclear_up/article/details/49201357

前言

所謂的窗口(Window)就是一個(gè)顯示在手機(jī)屏幕上可視化視圖的一片區(qū)域。在Android中窗口是一個(gè)抽象的概念,每一個(gè)Activity就對(duì)應(yīng)著一個(gè)窗口,而所有的窗口都是由視圖(View)來呈現(xiàn),而我們知道View構(gòu)成的一個(gè)樹形結(jié)構(gòu)的視圖就組成了一個(gè)Activity的界面了。在Android系統(tǒng)中窗口分為三個(gè)類型:

  1. 應(yīng)用窗口:所謂應(yīng)用窗口指的就是該窗口對(duì)應(yīng)一個(gè)Activity,因此,要?jiǎng)?chuàng)建應(yīng)用窗口就必須在Activity中完成了。
  2. 子窗口:所謂子窗口指的是必須依附在某個(gè)父窗口之上。
  3. 系統(tǒng)窗口:所謂系統(tǒng)窗口指的是由系統(tǒng)進(jìn)程創(chuàng)建,不依賴于任何應(yīng)用或者不依附在任何父窗口之上。

在WindowManager類的內(nèi)部類LayoutParams中定義了以上三種窗口類型,我們暫且不管WindowManager類是干嘛的,直接看其內(nèi)部類LayoutParams的實(shí)現(xiàn)。內(nèi)部類LayoutParams其實(shí)是一組用于描述窗口(Window)參數(shù)的數(shù)據(jù)類,其中包括窗口的大小,類型,特征,軟鍵盤輸入法模式,相對(duì)位置以及動(dòng)畫,背景圖像格式等等。

》轉(zhuǎn)自:http://blog.csdn.net/feiduclear_up/article/details/49201357

前言

所謂的窗口(Window)就是一個(gè)顯示在手機(jī)屏幕上可視化視圖的一片區(qū)域。在Android中窗口是一個(gè)抽象的概念,每一個(gè)Activity就對(duì)應(yīng)著一個(gè)窗口,而所有的窗口都是由視圖(View)來呈現(xiàn),而我們知道View構(gòu)成的一個(gè)樹形結(jié)構(gòu)的視圖就組成了一個(gè)Activity的界面了。在Android系統(tǒng)中窗口分為三個(gè)類型:

  1. 應(yīng)用窗口:所謂應(yīng)用窗口指的就是該窗口對(duì)應(yīng)一個(gè)Activity,因此,要?jiǎng)?chuàng)建應(yīng)用窗口就必須在Activity中完成了。
  2. 子窗口:所謂子窗口指的是必須依附在某個(gè)父窗口之上。
  3. 系統(tǒng)窗口:所謂系統(tǒng)窗口指的是由系統(tǒng)進(jìn)程創(chuàng)建,不依賴于任何應(yīng)用或者不依附在任何父窗口之上。

在WindowManager類的內(nèi)部類LayoutParams中定義了以上三種窗口類型,我們暫且不管WindowManager類是干嘛的,直接看其內(nèi)部類LayoutParams的實(shí)現(xiàn)。內(nèi)部類LayoutParams其實(shí)是一組用于描述窗口(Window)參數(shù)的數(shù)據(jù)類,其中包括窗口的大小,類型,特征,軟鍵盤輸入法模式,相對(duì)位置以及動(dòng)畫,背景圖像格式等等。

1.窗口參數(shù)WinsowManager#LayoutParams

public interface WindowManager extends ViewManager {
    ...........
    public static class LayoutParams extends ViewGroup.LayoutParams
            implements Parcelable {

        //窗口的起點(diǎn)坐標(biāo)
        public int x;
        public int y;

        //以下定義都是描述窗口的類型
        public int type;
        //第一個(gè)應(yīng)用窗口
        public static final int FIRST_APPLICATION_WINDOW = 1;
        //所有程序窗口的base窗口,其他應(yīng)用程序窗口都顯示在它上面
        public static final int TYPE_BASE_APPLICATION   = 1;
        //所有Activity的窗口
        public static final int TYPE_APPLICATION        = 2;
        //目標(biāo)應(yīng)用窗口未啟動(dòng)之前的那個(gè)窗口
        public static final int TYPE_APPLICATION_STARTING = 3;
        //最后一個(gè)應(yīng)用窗口
        public static final int LAST_APPLICATION_WINDOW = 99;

        //第一個(gè)子窗口
        public static final int FIRST_SUB_WINDOW        = 1000;
        // 面板窗口,顯示于宿主窗口的上層
        public static final int TYPE_APPLICATION_PANEL  = FIRST_SUB_WINDOW;
        // 媒體窗口(例如視頻),顯示于宿主窗口下層
        public static final int TYPE_APPLICATION_MEDIA  = FIRST_SUB_WINDOW+1;
        // 應(yīng)用程序窗口的子面板,顯示于所有面板窗口的上層
        public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2;
        //對(duì)話框窗口
        public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3;
        //
        public static final int TYPE_APPLICATION_MEDIA_OVERLAY  = FIRST_SUB_WINDOW+4;
        //最后一個(gè)子窗口
        public static final int LAST_SUB_WINDOW         = 1999;

        //系統(tǒng)窗口,非應(yīng)用程序創(chuàng)建
        public static final int FIRST_SYSTEM_WINDOW     = 2000;
        //狀態(tài)欄,只能有一個(gè)狀態(tài)欄,位于屏幕頂端,其他窗口都位于它下方
        public static final int TYPE_STATUS_BAR         = FIRST_SYSTEM_WINDOW;
        //搜索欄,只能有一個(gè)搜索欄,位于屏幕上方
        public static final int TYPE_SEARCH_BAR         = FIRST_SYSTEM_WINDOW+1;
        //電話窗口,它用于電話交互(特別是呼入),置于所有應(yīng)用程序之上,狀態(tài)欄之下
        public static final int TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2;
        //系統(tǒng)警告提示窗口,出現(xiàn)在應(yīng)用程序窗口之上
        public static final int TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3;
        //鎖屏窗口
        public static final int TYPE_KEYGUARD           = FIRST_SYSTEM_WINDOW+4;
        //信息窗口,用于顯示Toast
        public static final int TYPE_TOAST              = FIRST_SYSTEM_WINDOW+5;
        //系統(tǒng)頂層窗口,顯示在其他一切內(nèi)容之上,此窗口不能獲得輸入焦點(diǎn),否則影響鎖屏
        public static final int TYPE_SYSTEM_OVERLAY     = FIRST_SYSTEM_WINDOW+6;
        //電話優(yōu)先,當(dāng)鎖屏?xí)r顯示,此窗口不能獲得輸入焦點(diǎn),否則影響鎖屏
        public static final int TYPE_PRIORITY_PHONE     = FIRST_SYSTEM_WINDOW+7;
        //系統(tǒng)對(duì)話框窗口
        public static final int TYPE_SYSTEM_DIALOG      = FIRST_SYSTEM_WINDOW+8;
        //鎖屏?xí)r顯示的對(duì)話框
        public static final int TYPE_KEYGUARD_DIALOG    = FIRST_SYSTEM_WINDOW+9;
        //系統(tǒng)內(nèi)部錯(cuò)誤提示,顯示在任何窗口之上
        public static final int TYPE_SYSTEM_ERROR       = FIRST_SYSTEM_WINDOW+10;
        //內(nèi)部輸入法窗口,顯示于普通UI之上,應(yīng)用程序可重新布局以免被此窗口覆蓋
        public static final int TYPE_INPUT_METHOD       = FIRST_SYSTEM_WINDOW+11;
        //內(nèi)部輸入法對(duì)話框,顯示于當(dāng)前輸入法窗口之上
        public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12;
        //墻紙窗口
        public static final int TYPE_WALLPAPER          = FIRST_SYSTEM_WINDOW+13;
        //狀態(tài)欄的滑動(dòng)面板
        public static final int TYPE_STATUS_BAR_PANEL   = FIRST_SYSTEM_WINDOW+14;
        //安全系統(tǒng)覆蓋窗口,這些窗戶必須不帶輸入焦點(diǎn),否則會(huì)干擾鍵盤
        public static final int TYPE_SECURE_SYSTEM_OVERLAY = FIRST_SYSTEM_WINDOW+15;
        //最后一個(gè)系統(tǒng)窗口
        public static final int LAST_SYSTEM_WINDOW      = 2999;

        ........

        //窗口特征標(biāo)記
        public int flags;
        //當(dāng)該window對(duì)用戶可見的時(shí)候,允許鎖屏
        public static final int FLAG_ALLOW_LOCK_WHILE_SCREEN_ON     = 0x00000001;
        //窗口后面的所有內(nèi)容都變暗
        public static final int FLAG_DIM_BEHIND        = 0x00000002;
        //Flag:窗口后面的所有內(nèi)容都變模糊
        public static final int FLAG_BLUR_BEHIND        = 0x00000004;
        //窗口不能獲得焦點(diǎn)
        public static final int FLAG_NOT_FOCUSABLE      = 0x00000008;
        //窗口不接受觸摸屏事件
        public static final int FLAG_NOT_TOUCHABLE      = 0x00000010;
        //即使在該window在可獲得焦點(diǎn)情況下,允許該窗口之外的點(diǎn)擊事件傳遞到當(dāng)前窗口后面的的窗口去
        public static final int FLAG_NOT_TOUCH_MODAL    = 0x00000020;
        //當(dāng)手機(jī)處于睡眠狀態(tài)時(shí),如果屏幕被按下,那么該window將第一個(gè)收到觸摸事件
        public static final int FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040;
        //當(dāng)該window對(duì)用戶可見時(shí),屏幕出于常亮狀態(tài)
        public static final int FLAG_KEEP_SCREEN_ON     = 0x00000080;
        //:讓window占滿整個(gè)手機(jī)屏幕,不留任何邊界
        public static final int FLAG_LAYOUT_IN_SCREEN   = 0x00000100;
        //允許窗口超出整個(gè)手機(jī)屏幕
        public static final int FLAG_LAYOUT_NO_LIMITS   = 0x00000200;
        //window全屏顯示
        public static final int FLAG_FULLSCREEN      = 0x00000400;
        //恢復(fù)window非全屏顯示
        public static final int FLAG_FORCE_NOT_FULLSCREEN   = 0x00000800;
        //開啟窗口抖動(dòng)
        public static final int FLAG_DITHER             = 0x00001000;
        //安全內(nèi)容窗口,該窗口顯示時(shí)不允許截屏
        public static final int FLAG_SECURE             = 0x00002000;

        //鎖屏?xí)r顯示該窗口
        public static final int FLAG_SHOW_WHEN_LOCKED = 0x00080000;
        //系統(tǒng)的墻紙顯示在該窗口之后
        public static final int FLAG_SHOW_WALLPAPER = 0x00100000;
        //當(dāng)window被顯示的時(shí)候,系統(tǒng)將把它當(dāng)做一個(gè)用戶活動(dòng)事件,以點(diǎn)亮手機(jī)屏幕
        public static final int FLAG_TURN_SCREEN_ON = 0x00200000;
        //該窗口顯示,消失鍵盤
        public static final int FLAG_DISMISS_KEYGUARD = 0x00400000;
        //當(dāng)該window在可以接受觸摸屏情況下,讓因在該window之外,而發(fā)送到后面的window的觸摸屏可以支持split touch
        public static final int FLAG_SPLIT_TOUCH = 0x00800000;
        //對(duì)該window進(jìn)行硬件加速,該flag必須在Activity或Dialog的Content View之前進(jìn)行設(shè)置
        public static final int FLAG_HARDWARE_ACCELERATED = 0x01000000;
        //讓window占滿整個(gè)手機(jī)屏幕,不留任何邊界
        public static final int FLAG_LAYOUT_IN_OVERSCAN = 0x02000000;
        //透明狀態(tài)欄
        public static final int FLAG_TRANSLUCENT_STATUS = 0x04000000;
        //透明導(dǎo)航欄
        public static final int FLAG_TRANSLUCENT_NAVIGATION = 0x08000000;

        ..........
        //軟輸入法模式
        public int softInputMode;

        //用于描述軟鍵盤顯示規(guī)則的bite的mask
        public static final int SOFT_INPUT_MASK_STATE = 0x0f;
        //沒有軟鍵盤顯示的約定規(guī)則
        public static final int SOFT_INPUT_STATE_UNSPECIFIED = 0;
        //可見性狀態(tài)softInputMode,請(qǐng)不要改變軟輸入?yún)^(qū)域的狀態(tài)
        public static final int SOFT_INPUT_STATE_UNCHANGED = 1;
        //用戶導(dǎo)航(navigate)到你的窗口時(shí)隱藏軟鍵盤
        public static final int SOFT_INPUT_STATE_HIDDEN = 2;
        //總是隱藏軟鍵盤
        public static final int SOFT_INPUT_STATE_ALWAYS_HIDDEN = 3;
        //用戶導(dǎo)航(navigate)到你的窗口時(shí)顯示軟鍵盤
        public static final int SOFT_INPUT_STATE_VISIBLE = 4;
        //總是顯示軟鍵盤
        public static final int SOFT_INPUT_STATE_ALWAYS_VISIBLE = 5;
        //顯示軟鍵盤時(shí)用于表示window調(diào)整方式的bite的mask
        public static final int SOFT_INPUT_MASK_ADJUST = 0xf0;
        //不指定顯示軟件盤時(shí),window的調(diào)整方式
        public static final int SOFT_INPUT_ADJUST_UNSPECIFIED = 0x00;
        //當(dāng)顯示軟鍵盤時(shí),調(diào)整window內(nèi)的控件大小以便顯示軟鍵盤
        public static final int SOFT_INPUT_ADJUST_RESIZE = 0x10;
        //當(dāng)顯示軟鍵盤時(shí),調(diào)整window的空白區(qū)域來顯示軟鍵盤,即使調(diào)整空白區(qū)域,軟鍵盤還是有可能遮擋一些有內(nèi)容區(qū)域,這時(shí)用戶就只有退出軟鍵盤才能看到這些被遮擋區(qū)域并進(jìn)行
        public static final int SOFT_INPUT_ADJUST_PAN = 0x20;
        //當(dāng)顯示軟鍵盤時(shí),不調(diào)整window的布局
        public static final int SOFT_INPUT_ADJUST_NOTHING = 0x30;
        //用戶導(dǎo)航(navigate)到了你的window
        public static final int SOFT_INPUT_IS_FORWARD_NAVIGATION = 0x100;

        //窗口的對(duì)齊方式
        public int gravity;

        //期望的位圖格式,默認(rèn)為不透明,參考android.graphics.PixelFormat
        public int format;
        //窗口所使用的動(dòng)畫設(shè)置,它必須是一個(gè)系統(tǒng)資源而不是應(yīng)用程序資源,因?yàn)榇翱诠芾砥鞑荒茉L問應(yīng)用程序
        public int windowAnimations;
        //整個(gè)窗口的半透明值,1.0表示不透明,0.0表示全透明
        public float alpha = 1.0f;
        //當(dāng)FLAG_DIM_BEHIND設(shè)置后生效,該變量指示后面的窗口變暗的程度,1.0表示完全不透明,0.0表示沒有變暗
        public float dimAmount = 1.0f;

        public static final float BRIGHTNESS_OVERRIDE_NONE = -1.0f;
        public static final float BRIGHTNESS_OVERRIDE_OFF = 0.0f;
        public static final float BRIGHTNESS_OVERRIDE_FULL = 1.0f;
        public float screenBrightness = BRIGHTNESS_OVERRIDE_NONE;
        //用來覆蓋用戶設(shè)置的屏幕亮度,表示應(yīng)用用戶設(shè)置的屏幕亮度,從0到1調(diào)整亮度從暗到最亮發(fā)生變化
        public float buttonBrightness = BRIGHTNESS_OVERRIDE_NONE;

        public static final int ROTATION_ANIMATION_ROTATE = 0;
        public static final int ROTATION_ANIMATION_CROSSFADE = 1;
        public static final int ROTATION_ANIMATION_JUMPCUT = 2;
        //屏幕旋轉(zhuǎn)動(dòng)畫
        public int rotationAnimation = ROTATION_ANIMATION_ROTATE;

        //窗口的標(biāo)示符
        public IBinder token = null;
        //此窗口所在應(yīng)用的包名
        public String packageName = null;
        //窗口屏幕方向
        public int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;

        //控制status bar是否可見,兩種賦值  View#STATUS_BAR_VISIBLE;View#STATUS_BAR_HIDDEN
        public int systemUiVisibility;

        ......

    }
}

分析:WindowManager.LayoutParams繼承自ViewGrop.LayoutParams用于描述Android窗口的參數(shù)。由上面代碼定義我們知道關(guān)于窗口有以下幾個(gè)重要的參數(shù):

  • width:描述窗口的寬度,該變量是父類ViewGroup.LayoutParams的成員變量。
  • height:描述窗口的高度,該變量同樣是父類ViewGroup.LayoutParams的成員變量。
  • x:描述窗口的起點(diǎn)X軸的坐標(biāo)。
  • y:描述窗口起點(diǎn)Y軸的坐標(biāo)。
  • type:窗口的類型,分為三個(gè)大類型:應(yīng)用窗口,子窗口,系統(tǒng)窗口。
  • flag:窗口特征標(biāo)記,比如是否全屏,是否隱藏標(biāo)題欄等。
  • gravity:窗口的對(duì)齊方式,居中還是置頂或者置底等等。

接下來從源碼角度分析Android系統(tǒng)中的以上三種窗口創(chuàng)建過程。

2.應(yīng)用窗口創(chuàng)建過程分析

2.1Activity,Window,WindowManager之間的關(guān)系

一個(gè)應(yīng)用窗口一般對(duì)應(yīng)一個(gè)Activity對(duì)象,因此要?jiǎng)?chuàng)建應(yīng)用窗口就必須創(chuàng)建一個(gè)Activity。由Activity的啟動(dòng)過程知道,所有Activity的啟動(dòng)都是有ActivityManagerService(簡(jiǎn)稱Ams)通過Bindler進(jìn)程間通信機(jī)制向客戶端進(jìn)程ActivityThread發(fā)送創(chuàng)建新的Activity對(duì)象通知,所有Activity的創(chuàng)建都在對(duì)應(yīng)應(yīng)用程序進(jìn)程ActivityThread類中完成。Activity類的創(chuàng)建完成之后會(huì)調(diào)用Activity#attach方法,代碼如下:

public class Activity extends ContextThemeWrapper
        implements LayoutInflater.Factory2,
        Window.Callback, KeyEvent.Callback,
        OnCreateContextMenuListener, ComponentCallbacks2,
        Window.OnWindowDismissedCallback {

    private Window mWindow;

    private WindowManager mWindowManager;

    ........
    final void attach(Context context, ActivityThread aThread,
            Instrumentation instr, IBinder token, int ident,
            Application application, Intent intent, ActivityInfo info,
            CharSequence title, Activity parent, String id,
            NonConfigurationInstances lastNonConfigurationInstances,
            Configuration config, String referrer, IVoiceInteractor voiceInteractor) {
        attachBaseContext(context);
        //----- 1 ------
        mWindow = new PhoneWindow(this);
        //----- 2 ------
        mWindow.setCallback(this);
        //----- 3 ------
        mWindow.setOnWindowDismissedCallback(this);
        mWindow.getLayoutInflater().setPrivateFactory(this);
        //----- 4 ------   
        mWindow.setWindowManager(            (WindowManager)context.getSystemService(Context.WINDOW_SERVICE),
                mToken, mComponent.flattenToString(),
                (info.flags & ActivityInfo.FLAG_HARDWARE_ACCELERATED) != 0);
        //----- 5 ------
        mWindowManager = mWindow.getWindowManager();
        .........
    }
    ........
}

分析:

1. 在attach放法中首先創(chuàng)建一個(gè)PhoneWindow對(duì)象賦值給Attivity的成員變量mWindow,而Window類是一個(gè)抽象類,用于描述屏幕頂層視圖和用戶行為操作的窗口,而PhoneWindow是實(shí)現(xiàn)了抽象類Window的子類。

2. 給Activity窗口Window類型的成員變量mWindow設(shè)置事件回調(diào)監(jiān)聽,而Activity實(shí)現(xiàn)了Window#Callback接口類,如此一來Activity就可以分發(fā)處理觸摸事件了,這就是為什么按back鍵當(dāng)前Activity會(huì)finish掉的原因。

3. 給Activity窗口Window類型的成員變量mWindow設(shè)置窗口的消失回調(diào)監(jiān)聽,用于處理Activity的窗口消失時(shí)finish掉當(dāng)前Activity。

4. 獲得當(dāng)前Activity的窗口管理對(duì)象WindowManager,然后給Window類設(shè)置窗口管理服務(wù),也就是賦值給Window類的成員變量mWindowManager。

5. 獲得Window類的窗口管理對(duì)象mWindowManager,然后賦值給Activity的成員變量mWindowManager,由此我們知道Activity的窗口管理和Window類的窗口管理指向的都是同一個(gè)WindowManager對(duì)象。

Window,WindowManager,Activity關(guān)系類圖如下:

[圖片上傳中...(image-c12d5f-1511334343081-2)]

Activity的成員變量mWindow類型為Window,該類就是用于描述應(yīng)用程序窗口類,因此一個(gè)Activity對(duì)應(yīng)著一個(gè)Window也就是應(yīng)用窗口。

Activity的成員變量mWindowManager類型為WindowManager,它用來管理當(dāng)前Activity的窗口,因此一個(gè)Activity也對(duì)應(yīng)著一個(gè)WindowManager窗口管理器。有前面分析得知Window類的成員變量mWindowManager和Activity的成員變量mWindowManager都是指向同一個(gè)WindowManager對(duì)象,而WindowManager對(duì)象是調(diào)用如下代碼獲?。?/p>

(WindowManager)context.getSystemService(Context.WINDOW_SERVICE)

而我們知道Activity是繼承Context類的,ContextImpl類中實(shí)現(xiàn)了WindowManager服務(wù)的注冊(cè),代碼如下:

class ContextImpl extends Context {
static {
    registerService(WINDOW_SERVICE, new ServiceFetcher() {
                Display mDefaultDisplay;
                public Object getService(ContextImpl ctx) {
                    Display display = ctx.mDisplay;
                    if (display == null) {
                        if (mDefaultDisplay == null) {
                            DisplayManager dm = (DisplayManager)ctx.getOuterContext().
                                    getSystemService(Context.DISPLAY_SERVICE);
                            mDefaultDisplay = dm.getDisplay(Display.DEFAULT_DISPLAY);
                        }
                        display = mDefaultDisplay;
                    }
                    return new WindowManagerImpl(display);
                }});
    .......
    }
}

有上面代碼可知,在ContextImpl類中注冊(cè)服務(wù)是一個(gè)靜態(tài)代碼塊,也就是說值執(zhí)行main方法之前就已經(jīng)完成了WindowManager服務(wù)的注冊(cè)。在整個(gè)應(yīng)用第一次創(chuàng)建Context對(duì)象的時(shí)候就已經(jīng)創(chuàng)建WindowManagerImpl對(duì)象了,然而WindowManager是個(gè)抽象類,具體實(shí)現(xiàn)指向WindowManagerImpl。整個(gè)應(yīng)用程序的窗口管理服務(wù)都是指向同一個(gè)WindowManagerImpl對(duì)象,言外之意,不管一個(gè)應(yīng)用程序中有多少個(gè)Activity,但只有一個(gè)WindowManagerImpl對(duì)象。

2.2 Activity窗口添加視圖View的過程分析

前面我們說了,一個(gè)Activity對(duì)應(yīng)著一個(gè)窗口(Window),而窗口只是一個(gè)抽象的概念,所有的窗口都有視圖(View)來呈現(xiàn),因此我們來分析下視圖View是怎么添加到窗口Window上的?

每個(gè)Activity都會(huì)調(diào)用setContextView方法來加載布局視圖,而這其實(shí)就是視圖View添加到Activity窗口上的一個(gè)過程。加載布局視圖代碼如下Activity#setContentView

 public void setContentView(int layoutResID) {
        getWindow().setContentView(layoutResID);
        initWindowDecorActionBar();
    }

由于getWindow方法獲得的是Activity的成員變量mWindow,而Window對(duì)象其實(shí)指向PhoneWindow類,因此這里僅僅做了一個(gè)轉(zhuǎn)發(fā),其實(shí)現(xiàn)在PhoneWindow#setContentView方法中。代碼如下:

 public void setContentView(int layoutResID) {
        if (mContentParent == null) {
            //給窗口安裝裝飾視圖DecorView
            installDecor();
        } else if (!hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            mContentParent.removeAllViews();
        }

        if (hasFeature(FEATURE_CONTENT_TRANSITIONS)) {
            final Scene newScene = Scene.getSceneForLayout(mContentParent, layoutResID,
                    getContext());
            transitionTo(newScene);
        } else {
            //加載xml布局視圖內(nèi)容到視圖容器mContentParent中
            mLayoutInflater.inflate(layoutResID, mContentParent);
        }
    }

分析:

1. 調(diào)用installDecor方法給窗口安裝視圖裝飾,所謂視圖裝飾指的就是界面上看到的標(biāo)題欄,導(dǎo)航欄Actionbar,也就是窗口的標(biāo)題欄;而視圖裝飾的內(nèi)容就是layout.xml布局,也就是窗口所加載的視圖View。

installDecor方法實(shí)現(xiàn)的代碼如下:

 private void installDecor() {
        if (mDecor == null) {
            //
            mDecor = generateDecor();
            mDecor.setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
            mDecor.setIsRootNamespace(true);
            if (!mInvalidatePanelMenuPosted && mInvalidatePanelMenuFeatures != 0) {
                mDecor.postOnAnimation(mInvalidatePanelMenuRunnable);
            }
        }
        if (mContentParent == null) {
            mContentParent = generateLayout(mDecor);
         }
        ........
}

該方法中主要做了兩件事:

  • 調(diào)用generateDecor方法獲得DecorView對(duì)象,該對(duì)象是ViewGroup類型,用于描述窗口Window的頂層視圖。
  • 調(diào)用generateLayout方法給窗口添加標(biāo)題欄,同時(shí)獲得裝載窗口內(nèi)容的容器mContentParent,該成員變量也是ViewGroup類型。

2.調(diào)用inflate方法將布局視圖內(nèi)容加載到剛剛獲得的內(nèi)容容器mContentParent上。

到此,Activity窗口Window添加視圖View的流程就結(jié)束了。現(xiàn)總結(jié)一下他們之間的關(guān)系:一個(gè)Activity中有一個(gè)Window對(duì)象用于描述當(dāng)前應(yīng)用的窗口,而Window是抽象類,實(shí)際上指向PhoneWindow類。PhoneWindow類中有一個(gè)內(nèi)部類DecorView用于描述窗口的頂層視圖,該類實(shí)際上是ViewGroup類型。當(dāng)往DecorView上添加標(biāo)題欄和窗口內(nèi)容容器之后,最后將layout.xml布局添加到窗口的內(nèi)容容器中,即完成了窗口Window添加視圖View了。最后用之前博客中的一張圖片來描述以上關(guān)系:

這里寫圖片描述

【轉(zhuǎn)載請(qǐng)注明出處:http://blog.csdn.net/feiduclear_up

CSDN 廢墟的樹】

2.3 Activity添加窗口Window的過程

有上一小節(jié)知道一個(gè)Activity對(duì)應(yīng)著一個(gè)窗口Window,而窗口Window也完成了視圖View的添加,最后視圖View是怎么繪制到手機(jī)屏幕上的呢?

在Activity創(chuàng)建完成之后,Ams利用Bindler進(jìn)程間的通信機(jī)制通知客戶端調(diào)用ActivityThread類中的handleResumeActivity方法來啟動(dòng)Activity。代碼如下:

final void handleResumeActivity(IBinder token,
            boolean clearHide, boolean isForward, boolean reallyResume) {
        ........
        ActivityClientRecord r = performResumeActivity(token, clearHide);

        if (r != null) {
            final Activity a = r.activity;

            final int forwardBit = isForward ?
                    WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION : 0;
            boolean willBeVisible = !a.mStartedActivity;
            if (!willBeVisible) {
                try {
                    willBeVisible = ActivityManagerNative.getDefault().willActivityBeVisible(
                            a.getActivityToken());
                } catch (RemoteException e) {
                }
            }
            if (r.window == null && !a.mFinished && willBeVisible) {
                //客戶端Activity的窗口Window對(duì)象
                r.window = r.activity.getWindow();
                //窗口的頂層視圖DecorView對(duì)象
                View decor = r.window.getDecorView();
                decor.setVisibility(View.INVISIBLE);
                //客戶端Activity窗口的窗口管理器對(duì)象WindowManager
                ViewManager wm = a.getWindowManager();
                //窗口的參數(shù)
                WindowManager.LayoutParams l = r.window.getAttributes();
                a.mDecor = decor;
                //窗口的類型為基礎(chǔ)應(yīng)用窗口
                l.type = WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
                //窗口的軟輸入法模式
                l.softInputMode |= forwardBit;
                if (a.mVisibleFromClient) {
                    //標(biāo)記客戶端Activity添加窗口成功
                    a.mWindowAdded = true;
                    //添加窗口
                    wm.addView(decor, l);
                }
    ........
        }
    }

分析:

首先獲得客戶端Activity的窗口對(duì)象Window,然后獲得該窗口的頂層視圖DecorView賦值給本地變量decor,再獲得客戶端Activity的窗口管理對(duì)象WindowManager賦值給本地變量wm,之后再來構(gòu)造窗口Window的參數(shù)WindowManager.LayoutParams,將當(dāng)前窗口的類型type賦值為TYPE_BASE_APPLICATION,由文章開頭我們知道該類型就是應(yīng)用窗口類型。最后調(diào)用WindowManager類中的addView方法完成當(dāng)前窗口Window的添加。

在這個(gè)過程中最主要的操作就是調(diào)用了WindowManager#addView方法,而WindowManager是個(gè)接口類,繼承自ViewManager。

ViewManager源碼如下:

public interface ViewManager
{
    public void addView(View view, ViewGroup.LayoutParams params);
    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
    public void removeView(View view);
}

該接口類僅僅定義了三個(gè)接口方法:

  • addView:添加View視圖
  • updateViewLayout:更新視圖
  • removeView:移除視圖

所以該類是用來管理視圖View狀態(tài)的類,也就是管理視圖View的添加,更新,移除。從此處也可以看出,所謂的WindowManager窗口管理其實(shí)就是對(duì)窗口的視圖View進(jìn)行管理。

WindowManager類源碼如下:

public interface WindowManager extends ViewManager {
    ........
     public Display getDefaultDisplay();
     public void removeViewImmediate(View view);
     public static class LayoutParams extends ViewGroup.LayoutParams
            implements Parcelable {
    ........
    }
}

該類繼承自ViewManager,也是一個(gè)接口類,里面定義了兩個(gè)接口方法和一個(gè)內(nèi)部類LayoutParams類用來描述窗口Window參數(shù)。關(guān)于WindowManager真正的實(shí)現(xiàn)其實(shí)在WindowManagerImpl類中。

WindowManagerImpl源碼如下:

public final class WindowManagerImpl implements WindowManager {
    private final WindowManagerGlobal mGlobal = WindowManagerGlobal.getInstance();
    private final Display mDisplay;

    private IBinder mDefaultToken;

    public WindowManagerImpl(Display display) {
        this(display, null);
    }

    private WindowManagerImpl(Display display, Window parentWindow) {
        mDisplay = display;
        mParentWindow = parentWindow;
    }

    public WindowManagerImpl createLocalWindowManager(Window parentWindow) {
        return new WindowManagerImpl(mDisplay, parentWindow);
    }

 @Override
    public void addView(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.addView(view, params, mDisplay, mParentWindow);
    }

    @Override
    public void updateViewLayout(@NonNull View view, @NonNull ViewGroup.LayoutParams params) {
        applyDefaultToken(params);
        mGlobal.updateViewLayout(view, params);
    }
@Override
    public void removeView(View view) {
        mGlobal.removeView(view, false);
    }

    @Override
    public void removeViewImmediate(View view) {
        mGlobal.removeView(view, true);
    }

    @Override
    public Display getDefaultDisplay() {
        return mDisplay;
    }
}

分析:

該來中實(shí)現(xiàn)了接口類ViewManager中的三個(gè)方法。但是細(xì)心的你會(huì)發(fā)現(xiàn),其實(shí)這三個(gè)方法內(nèi)部將實(shí)際操作轉(zhuǎn)發(fā)到mClobal對(duì)象的三個(gè)對(duì)應(yīng)的方法中去了,而mGlobal成員變量的類型是WindowManagerGlobal類。

WindowManagerGlobal類源碼:

public final class WindowManagerGlobal {
    private final ArrayList<View> mViews = new ArrayList<View>();
    private final ArrayList<ViewRootImpl> mRoots = new ArrayList<ViewRootImpl>();
    private final ArrayList<WindowManager.LayoutParams> mParams =
            new ArrayList<WindowManager.LayoutParams>();
    ........
    //單例模式構(gòu)造方法
    public static WindowManagerGlobal getInstance() {
        synchronized (WindowManagerGlobal.class) {
            if (sDefaultWindowManager == null) {
                sDefaultWindowManager = new WindowManagerGlobal();
            }
            return sDefaultWindowManager;
        }
    }
     ........
     //窗口的添加過程
     public void addView(View view, ViewGroup.LayoutParams params,
            Display display, Window parentWindow) {
        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;
        if (parentWindow != null) {
            parentWindow.adjustLayoutParamsForSubWindow(wparams);
        } else {
            final Context context = view.getContext();
            if (context != null
                    && context.getApplicationInfo().targetSdkVersion >= Build.VERSION_CODES.LOLLIPOP) {
                wparams.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
            }
        }

        ViewRootImpl root;
        View panelParentView = null;
        synchronized (mLock) {    
            if (mSystemPropertyUpdater == null) {
                mSystemPropertyUpdater = new Runnable() {
                    @Override public void run() {
                        synchronized (mLock) {
                            for (int i = mRoots.size() - 1; i >= 0; --i) {
                                mRoots.get(i).loadSystemProperties();
                            }
                        }
                    }
                };

            }
            //不能重復(fù)添加窗口
            int index = findViewLocked(view, false);
            if (index >= 0) {
                if (mDyingViews.contains(view)) {
                    mRoots.get(index).doDie();
                } else {
                    throw new IllegalStateException("View " + view
                            + " has already been added to the window manager.");
                }       
            }
            //判斷當(dāng)前窗口是否為子窗口,如果是則獲得其父窗口并保存在panelParentView變量中
            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
                final int count = mViews.size();
                for (int i = 0; i < count; i++) {
                    if (mRoots.get(i).mWindow.asBinder() == wparams.token) {
                        panelParentView = mViews.get(i);
                    }
                }
            }
            //每一個(gè)窗口對(duì)應(yīng)著一個(gè)ViewRootImpl對(duì)象
            root = new ViewRootImpl(view.getContext(), display);
            //給當(dāng)前窗口視圖設(shè)置參數(shù)
            view.setLayoutParams(wparams);
            //保存三個(gè)數(shù)組
            mViews.add(view);
            mRoots.add(root);
            mParams.add(wparams);
        }

        try {
            //真正執(zhí)行窗口的視圖View繪制工作的方法
            root.setView(view, wparams, panelParentView);
        } 
    }

 //主要更新當(dāng)前窗口的參數(shù)LayoutParams
 public void updateViewLayout(View view, ViewGroup.LayoutParams params) {

        final WindowManager.LayoutParams wparams = (WindowManager.LayoutParams)params;

        view.setLayoutParams(wparams);

        synchronized (mLock) {
            int index = findViewLocked(view, true);
            ViewRootImpl root = mRoots.get(index);
            mParams.remove(index);
            mParams.add(index, wparams);
            root.setLayoutParams(wparams, false);
        }
    }
    //從三個(gè)數(shù)組里面分別移除DecorView對(duì)象,ViewRootIpl對(duì)象,WindowManager.LayoutParams對(duì)象
    public void removeView(View view, boolean immediate) { 
        synchronized (mLock) {
            int index = findViewLocked(view, true);
            View curView = mRoots.get(index).getView();
            removeViewLocked(index, immediate);
            if (curView == view) {
                return;
            }
        }
    }

分析:

在WindowManagerGlobal類中維系著三個(gè)數(shù)組分別是:

  • mViews:保存著當(dāng)前應(yīng)用所有窗口的頂層視圖DecorView對(duì)象
  • mRoots:保存著當(dāng)前應(yīng)用所有窗口的視圖繪制類ViewRootImpl
  • mParams:保存著當(dāng)前應(yīng)用所有窗口的參數(shù) WindowManager.LayoutParams

在2.1小節(jié)的最后我們知道,一個(gè)應(yīng)用中不管有多少個(gè)Activity,都共用一個(gè)WindowManagerImpl對(duì)象,而在WindowManagerImpl類中是單例模式獲得WindowManagerGlobal對(duì)象,因此:一個(gè)應(yīng)用中也就只有一個(gè)WindowManagerGlobal對(duì)象了。而在該對(duì)象中通過維護(hù)以上三個(gè)數(shù)組來維護(hù)一個(gè)應(yīng)用中所有窗口的管理。

addView方法:

  • 首先檢查當(dāng)前窗口是否已經(jīng)添加過,不允許重復(fù)添加窗口。
  • 判斷如果添加的窗口為子窗口類型,則找到他的父窗口,然后保存在變量panelParentView中作為后面setView方法的參數(shù)。
  • 在該方法中創(chuàng)建ViewRootImpl對(duì)象,每一個(gè)Window對(duì)象都持有一個(gè)ViewRootImpl對(duì)象,然后為當(dāng)前窗口視圖設(shè)置窗口參數(shù),在將當(dāng)前窗口視圖View,ViewRootImpl對(duì)象,以及窗口參數(shù)分別保存到三個(gè)數(shù)組里。
  • 最后調(diào)用ViewRootImpl#setView方法來完成當(dāng)前窗口視圖的繪制。ViewRootImpl類里面會(huì)依次執(zhí)行measure(測(cè)量),layout(布局),draw(繪制)三個(gè)方法來完成視圖View繪制到手機(jī)屏上的一個(gè)過程,此處不詳解,具體參考博客:從ViewRootImpl類分析View繪制的流程。此處才是真正將視圖View繪制到手機(jī)屏幕的地方。

updateViewLayout方法:

先找到指定的窗口視圖View,然后將舊的參數(shù)wparams從數(shù)組mParams移除,往數(shù)組mParams添加新的窗口參數(shù),最后給窗口視圖重新設(shè)置一下參數(shù)即完成了窗口視圖的更新操作。

removeView方法:

依次將三個(gè)參數(shù)從數(shù)組中移除,然后調(diào)用相應(yīng)的方法銷毀視圖View的繪制。

<a name="t6" target="_blank" style="user-select: text !important; text-decoration: none; box-sizing: border-box; color: rgb(12, 137, 207);"></a>總結(jié)

  1. 一個(gè)Activity就對(duì)應(yīng)著一個(gè)應(yīng)用窗口PhoneWindow對(duì)象,該對(duì)象用于描述窗口的。

  2. PhoneWindow類內(nèi)部有一個(gè)DecorView類,該類是ViewGroup類型,用于描述窗口的頂層視圖的,該視圖才是真正裝載layout.xml布局內(nèi)容的。

  3. 一個(gè)應(yīng)用不管有多少個(gè)Activity,都只有一個(gè)WindowManager窗口管理器,也就只有一個(gè)WindowManagerGlobal對(duì)象通過維護(hù)三個(gè)數(shù)組 mViews,mRoots,mParams來管理所有窗口的添加,更新,刪除。

  4. 一個(gè)窗口對(duì)應(yīng)著一個(gè)ViewRootImpl類,該類主要的作用就是將窗口的頂層視圖DecorView內(nèi)容繪制到手機(jī)屏幕上。

最后給出以上所有類圖關(guān)系圖如下:

這里寫圖片描述
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容