DialogFragment顯示不出StatusBar

場(chǎng)景

在業(yè)務(wù)框架搭建下馆铁,App只有一個(gè)Activity,各種頁面都是Fragment與Fragment交互企蹭,在直播場(chǎng)景交互復(fù)雜情況下又兵,在直播頁面的Fragment1可以點(diǎn)擊調(diào)整各種頁面Fragment2、Fragment3的場(chǎng)景下枫笛,假設(shè)采取manager.replace方法吨灭,會(huì)導(dǎo)致Fragment1在收到通知時(shí)候無法做出相應(yīng)用戶提示操作:彈窗、toast等刑巧,如果你硬要彈會(huì)崩潰的喧兄,親。所以頁面Fragment2啊楚、Fragment3都采用DialogFragment來進(jìn)行用戶交互吠冤。

DialogFragment

Window(簡單介紹,不在本章介紹范圍)
  • Window級(jí)別會(huì)拆分多個(gè)等級(jí)恭理,設(shè)置不同等級(jí)會(huì)有不同的重疊效果拯辙。Window統(tǒng)一由WindowManager進(jìn)行管理,大家有興趣可以點(diǎn)擊??地址Window學(xué)習(xí)地址
  • Window級(jí)別Dialog颜价、popupWindow涯保、toast之所以能懸浮在Activity上面,那是因?yàn)樗鼈兌几髯詣?chuàng)建屬于自己的Window周伦,而Activity的創(chuàng)建也是會(huì)創(chuàng)建一個(gè)系統(tǒng)級(jí)別的Window夕春,android系統(tǒng)window只是接口,唯一實(shí)現(xiàn)該接口是phoneWindow類专挪,所以具體邏輯我們都應(yīng)該去PhoneWindow里面查看及志。
  • 視圖顯示大概流程:創(chuàng)建一個(gè)PhoneWindow,根據(jù)用戶使用的Window的style里面的屬性值來進(jìn)行flag設(shè)置寨腔、features設(shè)置速侈、window的width、height設(shè)置迫卢。后續(xù)根據(jù)上面的features值對(duì)應(yīng)的來加載layoutResource(Layout Inspector中的DecorView點(diǎn)擊展開后的整體布局)對(duì)應(yīng)布局作為DecorView的內(nèi)容倚搬,從DecorView的內(nèi)容(layoutResource,其實(shí)就是我們?nèi)粘i_發(fā)的layout的xml)中findViewById找到拿來裝載開發(fā)者視圖的ViewGroup靖避,有趣的點(diǎn)就是google開發(fā)者把所有相關(guān)的xml布局中裝載開發(fā)者視圖的ViewGroup的id都命名為com.android.internal.R.id.content(大家很熟悉的Window類的static值:ID_ANDROID_CONTENT)
//代碼在PhoneWindow的generateLayout函數(shù)里
int layoutResource;
        int features = getLocalFeatures();
        // System.out.println("Features: 0x" + Integer.toHexString(features));
        if ((features & (1 << FEATURE_SWIPE_TO_DISMISS)) != 0) {
            layoutResource = R.layout.screen_swipe_dismiss;
            setCloseOnSwipeEnabled(true);
        } else if ((features & ((1 << FEATURE_LEFT_ICON) | (1 << FEATURE_RIGHT_ICON))) != 0) {
            if (mIsFloating) {
                TypedValue res = new TypedValue();
                getContext().getTheme().resolveAttribute(
                        R.attr.dialogTitleIconsDecorLayout, res, true);
                layoutResource = res.resourceId;
            } else {
                layoutResource = 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) {
  ……


//代碼在在PhoneWindow的generateLayout函數(shù)里潭枣,函數(shù)最后會(huì)把這個(gè)container返回,拿到container你想加什么都行拉。
//詳細(xì)的大家可以自行搜索幻捏,有很多人寫這部分寫的很好盆犁,我就不在這里講了。
ViewGroup contentParent = (ViewGroup)findViewById(ID_ANDROID_CONTENT);
layoutResource含義圖解
Window的問題
  1. 剛剛上面都提了篡九,系統(tǒng)源碼會(huì)根據(jù)開發(fā)者設(shè)置的window style的一些屬性做flag設(shè)置谐岁、features設(shè)置、window的width榛臼、height設(shè)置伊佃。工作上因?yàn)閯e人復(fù)制一大坨window的style,里面定義了各種window的style屬性值沛善,其中就包含我們今天的主角android:windowIsFloating航揉。windowsIsFloating是表示W(wǎng)indow彈窗是否懸浮狀態(tài)出現(xiàn)。

  1. windowIsFloating的屬性值在PhoneWindow中是有著重要意義的金刁,稍有不慎用錯(cuò)帅涂,就會(huì)出現(xiàn)文章標(biāo)題的問題。我們看下window這個(gè)屬性值有什么作用尤蛮。
//PhoneWindow的generateLayout函數(shù)
protected ViewGroup generateLayout(DecorView decor) {
         //省略部分源碼
        ……
        mIsFloating = a.getBoolean(R.styleable.Window_windowIsFloating, false);
        int flagsToUpdate = (FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR)
                & (~getForcedWindowFlags());
        if (mIsFloating) {
            //如果開發(fā)者設(shè)置windowIsFloating為true媳友,window內(nèi)容的大小就為內(nèi)容自身大小
            setLayout(WRAP_CONTENT, WRAP_CONTENT);
            //設(shè)置Window的Flag值,大家開發(fā)這么久都知道产捞,F(xiàn)lag是可以影響Window里內(nèi)容如何展示的:沉浸式醇锚、透明狀態(tài)欄等
            setFlags(0, flagsToUpdate);
        } else {
            setFlags(FLAG_LAYOUT_IN_SCREEN|FLAG_LAYOUT_INSET_DECOR, flagsToUpdate);
        }
        //省略部分源碼
        ……
}

  1. 分析下FLAG_LAYOUT_IN_SCREENFLAG_LAYOUT_INSET_DECOR這兩個(gè)常量作用,直接看接口注解就可以一目了然了坯临。
    FLAG_LAYOUT_IN_SCREEN:窗口標(biāo)志:將窗口置于整個(gè)屏幕中焊唬,忽略邊框周圍的裝飾(例如狀態(tài)欄)。窗口必須正確放置其內(nèi)容才能考慮屏幕裝飾看靠。如{@link Window#setFlags}中所述求晶,通常由Window為您設(shè)置此標(biāo)志。
    FLAG_LAYOUT_INSET_DECOR:窗口標(biāo)志:僅與{@link #FLAG_LAYOUT_IN_SCREEN}結(jié)合使用的特殊選項(xiàng)衷笋。在請(qǐng)求布局時(shí)屏幕上的窗口可能會(huì)出現(xiàn)在屏幕裝飾的上方或下方例如狀態(tài)欄芳杏。通過還包括此標(biāo)志,窗口經(jīng)理將報(bào)告所需的插入矩形辟宗,以確保屏幕裝飾不覆蓋您的內(nèi)容爵赵。該標(biāo)志通常是*按照{(diào)@link Window#setFlags}中的說明,由Window為您設(shè)置泊脐。
    理解:在保證開發(fā)者視圖完整性的情況下空幻,才會(huì)去考慮布局問題。當(dāng)兩個(gè)flag結(jié)合一起使用的話容客,就能看到我最終想要的效果圖:statusBar在頂部占一定高度秕铛,我自己的視圖在StatusBar下面進(jìn)行展示约郁,兩者相互不重合,不影響對(duì)方但两。如果mIsFloating為true的話鬓梅,那系統(tǒng)就只能讓開發(fā)者的視圖大小即為wrap_content(開發(fā)者視圖的xml的根布局為match_parent都沒有,因?yàn)檠b載開發(fā)者視圖的容器是wrap_content)谨湘,所以就算我按照staturBarColor的說明來使用绽快,設(shè)置各種flag值,依舊statusBar沒有反應(yīng)紧阔。

  1. 為什么上面說狀態(tài)欄是裝飾物坊罢?帶著這個(gè)疑問氮惯,我們繼續(xù)深入學(xué)習(xí)蝌数。
  • 首先我們要找到statusBar怎么創(chuàng)建出來的,我愣是在PhoneWindow找呀找呀找嫂丙,都一直沒找到相關(guān)的乖仇。還好換下思路诱鞠,跟著staturBarColor去找,終于給我找到statusBar的代碼了这敬。
  • 實(shí)際上我們?cè)谏鲜?strong>layoutResource含義圖解的圖片可以看到statusBar是以一個(gè)名為statusBarBackground的View形式出現(xiàn)的航夺,而且是和我們的layoutResource同一個(gè)層次。那么StatusBar就應(yīng)該是在DecorView中創(chuàng)建的拉崔涂,果不其然阳掐,我們?cè)贒ecorView里面找到我們要的代碼。
//DecorView的屬性值冷蚂,ColorViewState只是封裝一層數(shù)據(jù)結(jié)構(gòu)缭保,有興趣可以自己去看下源碼,不難的蝙茶,無非就是:View引用艺骂、設(shè)置位置參數(shù)、id隆夯、展示形式等钳恕。
    private final ColorViewState mStatusColorViewState =
            new ColorViewState(STATUS_BAR_COLOR_VIEW_ATTRIBUTES);
    private final ColorViewState mNavigationColorViewState =
            new ColorViewState(NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES);

  1. 為什么說statusBar和Navigation是裝飾者呢?因?yàn)閷?duì)于PhoneWindow來說完全不關(guān)心這兩個(gè)View長什么樣子蹄衷,是否添加都不關(guān)心忧额,所以對(duì)于PhoneWindow來講statusBar和Navigation即是裝飾者。再來看看DecorView對(duì)兩個(gè)裝飾者如何操作愧口?
//DecorView的updateColorViewInt函數(shù)中
private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
            int dividerColor, int size, boolean verticalBar, boolean seascape, int sideMargin,
            boolean animate, boolean force) {
            //省略部分代碼
            ……
            //view是Navigation or StatusBar
            View view = state.view;
            int resolvedHeight = verticalBar ? LayoutParams.MATCH_PARENT : size;
            int resolvedWidth = verticalBar ? size : LayoutParams.MATCH_PARENT;
            int resolvedGravity = verticalBar
                ? (seascape ? state.attributes.seascapeGravity : state.attributes.horizontalGravity)
                : state.attributes.verticalGravity;
           //如果當(dāng)前Window的statusBar和Navigation還沒被創(chuàng)建睦番,先創(chuàng)建再addView
           if (view == null) {
            if (showView) {
                state.view = view = new View(mContext);
                ……
                addView(view, lp);
                updateColorViewTranslations();
            }
        } else {
              //省略部分代碼
              ……
              if (lp.height != resolvedHeight || lp.width != resolvedWidth
                    || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin
                    || lp.leftMargin != leftMargin) {
                lp.height = resolvedHeight;
                lp.width = resolvedWidth;
                lp.gravity = resolvedGravity;
                lp.rightMargin = rightMargin;
                lp.leftMargin = leftMargin;
                view.setLayoutParams(lp);
            }
            if (showView) {
                setColor(view, color, dividerColor, verticalBar, seascape);
            }
        }
       //下面就是過渡動(dòng)畫了
       if (visibilityChanged) {
            view.animate().cancel();
             //省略部分代碼
              …… 
            } else {
                view.setAlpha(1.0f);
                view.setVisibility(showView ? VISIBLE : INVISIBLE);
            }
        }
      //state所有屬性值均賦值完成
      state.visible = show;
      state.color = color;
}

  1. 經(jīng)過第五步的源碼分析,statusBar和Navigation已經(jīng)添加到我們的DecorView里面了。那剩下的問題就只有托嚣,添加了statusBar后我們的裝載開發(fā)者視圖的ViewGroup(不懂ViewGroup是什么的看Window標(biāo)題下的視圖顯示大概流程)如何增加自己的top和bottom的間距巩检,讓開發(fā)者視圖不與statusBar和Navigation重疊呢?
//DecorView的updateColorViews函數(shù)里
WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
        //省略部分代碼
        …… 
        //根據(jù)window or view的flag值示启,算出Navigation狀態(tài)值兢哭,是否消費(fèi)部分View空間
        boolean hideNavigation = (sysUiVisibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;
        boolean forceConsumingNavBar = (mForceWindowDrawsBarBackgrounds
                        && (attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
                        && (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
                        && !hideNavigation)
                || (mLastShouldAlwaysConsumeSystemBars && hideNavigation);
      
        boolean consumingNavBar =
                ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
                        && (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
                        && !hideNavigation)
                || forceConsumingNavBar;
        //根據(jù)window or view的flag值,算出statusBar狀態(tài)值丑搔,是否消費(fèi)部分View空間
        boolean fullscreen = (sysUiVisibility & SYSTEM_UI_FLAG_FULLSCREEN) != 0
                || (attrs.flags & FLAG_FULLSCREEN) != 0;
        boolean consumingStatusBar = (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0
                && (attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0
                && (attrs.flags & FLAG_LAYOUT_INSET_DECOR) == 0
                && mForceWindowDrawsBarBackgrounds
                && mLastTopInset != 0
                || (mLastShouldAlwaysConsumeSystemBars && fullscreen);
        //根據(jù)上面的參數(shù),計(jì)算出staturBar高度提揍,Navigation的高度和左右邊距
        int consumedTop = consumingStatusBar ? mLastTopInset : 0;
        int consumedRight = consumingNavBar ? mLastRightInset : 0;
        int consumedBottom = consumingNavBar ? mLastBottomInset : 0;
        int consumedLeft = consumingNavBar ? mLastLeftInset : 0;
        //下面就是純粹的根據(jù)if else來改變裝載開發(fā)者視圖的ViewGroup的layoutParams了(這一步就回到大家熟悉的地方了)
        if (mContentRoot != null
                && mContentRoot.getLayoutParams() instanceof MarginLayoutParams) {
            MarginLayoutParams lp = (MarginLayoutParams) mContentRoot.getLayoutParams();
            if (lp.topMargin != consumedTop || lp.rightMargin != consumedRight
                    || lp.bottomMargin != consumedBottom || lp.leftMargin != consumedLeft) {
                lp.topMargin = consumedTop;
                lp.rightMargin = consumedRight;
                lp.bottomMargin = consumedBottom;
                lp.leftMargin = consumedLeft;
                mContentRoot.setLayoutParams(lp);
                 //省略部分代碼
                  …… 
            }
            if (insets != null) {
                //竟然都消耗PhoneWindow的部分區(qū)域啤月,那就要告訴PhoneWindow呀,把消耗區(qū)域值設(shè)置到insets里面(WindowInsets可自行查資料)
                insets = insets.inset(consumedLeft, consumedTop, consumedRight, consumedBottom);
            }
            //省略部分代碼
            …… 
            if (insets != null) {
                    //告訴PhoneWindow我把你該部分區(qū)域已經(jīng)消耗了劳跃,不可分配給別人了谎仲。
                   insets = insets.consumeStableInsets();
            }
            return insets;
        }
}

總結(jié)

上面說那么多,還沒提及怎么解決刨仑?那最后來個(gè)總結(jié)吧郑诺。既然知道windowIsFloating為true時(shí)候,是不添加statusBar和Navigation的杉武,那么我換成false即可辙诞。僅僅一個(gè)簡單style屬性類型,可以讓我們從PhoneWindow -> DecorView創(chuàng)建一路看過來轻抱,在遇到問題時(shí)候從源碼下手還是能學(xué)習(xí)到很多不錯(cuò)知識(shí)點(diǎn)的飞涂。
tips:當(dāng)你的DialogFragment為loading樣式、彈窗這些就不需要statusBar和Navigation了祈搜,可以直接設(shè)置為true较店。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市容燕,隨后出現(xiàn)的幾起案子梁呈,更是在濱河造成了極大的恐慌,老刑警劉巖蘸秘,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件官卡,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡醋虏,警方通過查閱死者的電腦和手機(jī)味抖,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來灰粮,“玉大人仔涩,你說我怎么就攤上這事≌持郏” “怎么了熔脂?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵佩研,是天一觀的道長。 經(jīng)常有香客問我霞揉,道長旬薯,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任适秩,我火速辦了婚禮绊序,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘秽荞。我一直安慰自己骤公,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布扬跋。 她就那樣靜靜地躺著阶捆,像睡著了一般。 火紅的嫁衣襯著肌膚如雪钦听。 梳的紋絲不亂的頭發(fā)上洒试,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音朴上,去河邊找鬼垒棋。 笑死,一個(gè)胖子當(dāng)著我的面吹牛痪宰,可吹牛的內(nèi)容都是我干的捕犬。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼酵镜,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼碉碉!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起淮韭,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤垢粮,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后靠粪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蜡吧,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年占键,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了昔善。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡畔乙,死狀恐怖君仆,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤返咱,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布钥庇,位于F島的核電站,受9級(jí)特大地震影響咖摹,放射性物質(zhì)發(fā)生泄漏评姨。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一萤晴、第九天 我趴在偏房一處隱蔽的房頂上張望吐句。 院中可真熱鬧,春花似錦店读、人聲如沸嗦枢。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽净宵。三九已至敲才,卻和暖如春裹纳,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背紧武。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國打工剃氧, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人阻星。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓朋鞍,卻偏偏與公主長得像,于是被迫代替她去往敵國和親妥箕。 傳聞我的和親對(duì)象是個(gè)殘疾皇子滥酥,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容