Android 將App的內容延伸到狀態欄/導航欄

來自我的CSDN博客:http://blog.csdn.net/dahaohan/article/details/52175190

看過Android的桌面應用都是介樣的:

如何讓自己的應用也達到這般效果呢?這里就介紹幾種常用的方法以及它們之間的區別。

首先展示下此次demo的布局和初始狀態:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.jokerlee.androidlsample.MainActivity"
    android:background="@android:color/holo_green_dark"
    android:fitsSystemWindows="true">


    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:text="Hello World."/>

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:text="Hello World."/>

</RelativeLayout>

初始效果圖如下:


透明狀態欄/導航欄##

使用這個方式首先要理解幾個概念,窗口層級以及窗口background/窗口透明:
Google在API-19 以及API-21新增對狀態欄/導航欄窗口透明和顏色的控制:

        getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
        getWindow().setStatusBarColor(blackColor);
        getWindow().setNavigationBarColor(blackColor);

對應的在主題內即可控制:

        <item name="android:statusBarColor">@android:color/transparent</item>
        <item name="android:navigationBarColor">@android:color/transparent</item>
        <item name="android:windowTranslucentStatus">true</item>
        <item name="android:windowTranslucentNavigation">true</item>

這里首先要明了這里狀態欄和導航欄窗口是系統級窗口而Activity對應的時應用窗口,它們屬于不同的窗口層級;
然后狀態欄/導航欄系統級窗口是在App應用窗口之上,故而Activity應用窗口雖然有整個屏幕的大小,但是可顯示內容的區域得除去其上疊加的不透明的窗口區域。詳細的窗口計算繪制可參考大神老羅的博文:
Android窗口管理服務WindowManagerService計算Activity窗口大小的過程分析

下面來使用主題控制導航欄/狀態欄透明,同時看看上述兩種設置透明的方式效果有何不同:

    <style name="TestTheme" parent="android:Theme.Material.NoActionBar">
        <!--下列兩行控制使得應用窗口透明,用于展示一些差異-->
        <item name="android:windowBackground">@color/transparent</item>
        <item name="android:windowIsTranslucent">true</item>
        <!--設置導航欄/狀態欄窗口color為透明-->
        <item name="android:statusBarColor">@android:color/transparent</item>
        <item name="android:navigationBarColor">@android:color/transparent</item>

        <!--<item name="android:windowTranslucentStatus">true</item>-->
        <!--<item name="android:windowTranslucentNavigation">true</item>-->
    </style>

初始桌面和啟動Activity效果圖:


可以看到雖然導航欄/狀態欄透明了,當時應用窗口顯示的內容依然只是除去了兩個系統窗口之外的區域,并沒有衍生到導航欄/狀態欄之下。

    //若是使用window flag控制導航欄/狀態欄透明
    <style name="TestTheme" parent="android:Theme.Material.NoActionBar">

        <!--下列兩行控制使得應用窗口透明,用于展示一些差異-->
        <item name="android:windowBackground">@color/transparent</item>
        <item name="android:windowIsTranslucent">true</item>
        <!--設置導航欄/狀態欄窗口color為透明-->
        <!--<item name="android:statusBarColor">@android:color/transparent</item>-->
        <!--<item name="android:navigationBarColor">@android:color/transparent</item>-->

        <item name="android:windowTranslucentStatus">true</item>
        <item name="android:windowTranslucentNavigation">true</item>
    </style>

效果如下:


可以看到已經將應用的內容布局延伸到導航欄/狀態欄下方了,來看看關于android:windowTranslucentStatus
android:windowTranslucentNavigation的官方說明看看來理解其與設置color transparent的區別:

        /**
         * Window flag: request a translucent status bar with minimal system-provided
         * background protection.
         *
         * <p>This flag can be controlled in your theme through the
         * {@link android.R.attr#windowTranslucentStatus} attribute; this attribute
         * is automatically set for you in the standard translucent decor themes
         * such as
         * {@link android.R.style#Theme_Holo_NoActionBar_TranslucentDecor},
         * {@link android.R.style#Theme_Holo_Light_NoActionBar_TranslucentDecor},
         * {@link android.R.style#Theme_DeviceDefault_NoActionBar_TranslucentDecor}, and
         * {@link android.R.style#Theme_DeviceDefault_Light_NoActionBar_TranslucentDecor}.</p>
         *
         * <p>When this flag is enabled for a window, it automatically sets
         * the system UI visibility flags {@link View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and
         * {@link View#SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN}.</p>
         */
         public static final int FLAG_TRANSLUCENT_STATUS = 0x04000000;

         * <p>When this flag is enabled for a window, it automatically sets
         * the system UI visibility flags {@link View#SYSTEM_UI_FLAG_LAYOUT_STABLE} and
         * {@link View#SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION}.</p>
         */
        public static final int FLAG_TRANSLUCENT_NAVIGATION = 0x08000000;

根據FLAG的說明,可以看出設置該標志位等同于View申請設置:

        //使得布局延伸到狀態欄和導航欄區域
        window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
        //透明狀態欄/導航欄
        window.setStatusBarColor(Color.TRANSPARENT);
        window.setNavigationBarColor(Color.TRANSPARENT);
        //這樣的效果跟上述的主題設置效果類似

PS:從效果圖看,雖然布局延伸到狀態欄導航欄區域,但是相應的內容“hello world”文字也被狀態欄/導航欄遮住了。在布局根視圖設置fitsSystemWindows為true可以使得,系統自動為視圖添加一個狀態欄/導航欄高度的padding:

rootView.setFitsSystemWindows(true);
//或者xml view標簽加入
android:fitsSystemWindows="true"

效果如下:


查看SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION 和 SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN的說明,可以發現其實還有兩個非常接近的FLAG:

public static final int SYSTEM_UI_FLAG_FULLSCREEN = 0x00000004;
public static final int SYSTEM_UI_FLAG_HIDE_NAVIGATION = 0x00000002;
//這兩flag跟上述主要區別在于,在設置和取消flag時,會引起重新布局,也就是當從SYSTEM_UI_FLAG_FULLSCREEN模式
//退出時,activity的內容顯示窗口會變小,看起來就是這個布局內容畫面會上移/下移。

根據官方的說明提示,SYSTEM_UI_FLAG_FULLSCREEN / SYSTEM_UI_FLAG_HIDE_NAVIGATION主要用于動態切換隱藏/顯示系統導航欄/狀態欄;例如書籍閱讀應用/視頻播放應用等。而像游戲類的全屏應用則推薦使用window flag。

API-19以下的如何實現?

上述的透明導航欄/狀態欄等API基本是需要API-19或是API-21才能使用的,這里還有一種API-1的方案能夠實現布局內容全屏:

        getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);

實際上只需要設置FLAG_LAYOUT_NO_LIMITS就足夠了;這FLAG是看Android原生的Launcher / Keyguard源碼,看到有用到如此設置,其窗口設置具體原理我也沒有弄清..... 有大神了解可以指點下。

PS:這個套路下,使用fitsSystemWindows="true"是無效的,智能自己控制號布局位置。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容