零散知識點總結(3) Android 狀態列知識點總結

澤毛發表於2017-12-21

一、概述

關於狀態列的討論很多,我們今天從需求的角度來看一下對於狀態列的知識點:

  • 不顯示狀態列(4.4)
  • 通過Window
  • 通過DecorView
  • 顯示狀態列
  • 靜態設定狀態列透明(4.4)windowTranslucentStatus,需要解決重疊問題
    • 手動設定padding
    • fitsSystemWindows
  • 設定狀態列顏色(5.0)
    • colorPrimaryDarkstyle中指定。
    • setStatusBarColor,動態設定。
  • 設定狀態列和根佈局的層級關係(4.4)
    • SYSTEM_UI_FLAG_VISIBLE:狀態列不覆蓋根佈局
    • SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN:狀態列覆蓋根佈局
  • 動態設定狀態列透明(5.0)
  • 改變狀態列圖示顏色
  • 官方做法(6.0)
  • 魅族/小米做法

二、不顯示狀態列

不顯示狀態列一般用於螢幕可顯示區域較小的時候,並且要求可見面積較大的情況,例如視訊、橫屏等,而不顯示狀態列的方法有兩種,下面我們分別介紹一下:

2.1 通過Activity所屬的Window

    public static void setStatusBarVisibleByWindow(Activity activity, boolean visible) {
        Window window = activity.getWindow();
        if (window != null) {
            WindowManager.LayoutParams winParams = window.getAttributes();
            final int bits = WindowManager.LayoutParams.FLAG_FULLSCREEN;
            if (visible) {
                winParams.flags &= ~bits;
            } else {
                winParams.flags |= bits;
            }
            window.setAttributes(winParams);
        }
    }
複製程式碼

2.2 通過DecorView

    public static void setStatusBarVisibleByDecorView(Activity activity, boolean visible) {
        Window window = activity.getWindow();
        if (window != null) {
            if (visible) {
                window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
            } else {
                window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN);
            }
        }
    }
複製程式碼

##2.3 小結 這兩種方法不可以混合使用,也就是說,不能通過其中一種方法將狀態列隱藏,又利用另一種方法將它顯示出來。

三、顯示狀態列

在顯示狀態列的情況下,我們將需求分為以下三類:

3.1 設定狀態列為透明

要求安卓版本大於4.4。 我們可以在style中指定,然後設定給Activity或者Application

    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="android:windowTranslucentStatus">true</item>
    </style>
複製程式碼

當然,也可以通過程式碼來指定:

    public static void setStatusBarTransparent(Activity activity) {
        Window window = activity.getWindow();
        if (window != null) {
            WindowManager.LayoutParams winParams = window.getAttributes();
            final int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
            winParams.flags |= bits;
            window.setAttributes(winParams);
        }
    }
複製程式碼

解決狀態列透明時和根佈局重疊的問題 這兩種方法是等效的,但是它們都存在一個問題,根佈局會和狀態列重疊,這時候就有兩種解決辦法:

3.1.1 在佈局中加上和狀態列高度相同的padding

這種方法有一點不好,就是我們需要事先知道狀態列的高度是多少,或者動態地獲取,並動態設定padding

3.1.2 fitsSystemWindows

為了解決上面的情況,我們可以通過fitsSystemWindows屬性讓系統自動根據狀態列的高度來給我們的佈局留下足夠的空間。

  • 在根佈局的xml中配置android:fitsSystemWindows屬性:
<LinearLayout android:fitsSystemWindows="true">
複製程式碼

3.1.3 小結

需要注意,使用上面的方法來試狀態列透明,需要在ActivityonCreate之前設定,如果在之後設定,那麼可能會出現意料之外的結果,如果想要在程式碼當中設定狀態列透明,那麼建議採用3.23.3的結合版本,也就是改變狀態列顏色為透明 + 改變佈局,我們分析完下面的兩種之後,再看一下怎麼在程式碼中設定。

3.2 改變狀態列的顏色

在狀態列不透明的情況下,我們可以通過下面的方法來改變狀態列的顏色,要求Android版本大於5.0

3.2.1 通過style設定

其中colorPrimaryDark就是指定的狀態列顏色

    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@android:color/holo_green_light</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>
複製程式碼

這裡有一點:如果我們設定了上面的屬性,並且設定了windowTranslucentStatus,即使不將fitSystemWindow設為true,也不會使狀態列和根佈局重疊。

3.2.2 通過程式碼動態設定

    public static void setStatusBarColor(Activity activity, int colorId) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Window window = activity.getWindow();
            if (window != null) {
                window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
                window.setStatusBarColor(activity.getResources().getColor(colorId));
            }
        }
    }
複製程式碼

3.2.3 小結

由於上面討論的兩種方法都只能在5.0以上實現,因此如果我們希望在5.0以下動態改變狀態列的顏色,那麼就只能通過3.1中的方法,把狀態列設定為透明,然後在佈局中新增一個和狀態列高度相同的背景,通過動態改變這個背景的顏色,來實現狀態列顏色的改變。

3.3 改變狀態列和根佈局的關係

其實在上面的兩種方法中,我們也間接地改變了狀態列和根佈局的關係,當然,我們也可以單純改變佈局關係,而不對狀態列作出任何改變:

    public static void setStatusBarOverlay(Activity activity, boolean overlay) {
        Window window = activity.getWindow();
        if (window != null) {
            if (overlay) {
                window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
            } else {
                window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
            }
        }
    }
複製程式碼

3.4 動態設定狀態列透明

由於這種方式結合了3.23.3,因此要求版本要大於5.0

    public static void setStatusBarTransparentDynamic(Activity activity, boolean overlay) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Window window = activity.getWindow();
            if (window != null) {
                window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
                if (!overlay) {
                    window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
                }
                window.setStatusBarColor(Color.TRANSPARENT);
            }
        }
    }
複製程式碼

四、改變狀態列圖示顏色

4.1 官方標準做法

這種方法要求AndroidAPI大於6.0

    public static void setStatusBarItemColor(Activity activity, boolean dark) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            Window window = activity.getWindow();
            if (window != null) {
                final int bits = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
                int currentBits = window.getDecorView().getSystemUiVisibility();
                if (dark) {
                    currentBits |= bits;
                } else {
                    currentBits &= ~bits;
                }
                window.getDecorView().setSystemUiVisibility(currentBits);
            }
        }
    }
複製程式碼

4.2 其它廠商做法

五、小結

5.1

總結下來,在顯示狀態列的情況下,適配範圍最廣的是下面這種方法:

  • 通過DecorView設定成透明並且重疊的模式(如3.4
  • 在根佈局中新增一個佈局,這個佈局的高度等於狀態列的高度,這樣我們就可以通過控制它的高度和顏色來實現多種組合。
  • 如果想要改變圖示的顏色,就只能在6.0或者廠商的方法。

5.2

對於狀態列的操作主要是兩種:一種是對DecorView操作,另一種是對window操作,使用的時候對除了設定狀態列顏色外的操作一定要保持統一,一定不要同時進行這兩種方法來進行相反的操作。舉個例子,如果採用了3.1中的對window進行操作使得狀態列透明,並通過fitSystemWindows改變佈局,那麼就不要再採用3.3DecorView.setSystemUiVisibility()的方式來改變佈局的結構,最好是成對地出現,否則會使得佈局出現意料之外的情況。

5.3

最後附上程式碼:

public class StatusBarUtils {

    public static void setStatusBarVisibleByWindow(Activity activity, boolean visible) {
        Window window = activity.getWindow();
        if (window != null) {
            WindowManager.LayoutParams winParams = window.getAttributes();
            final int bits = WindowManager.LayoutParams.FLAG_FULLSCREEN;
            if (visible) {
                winParams.flags &= ~bits;
            } else {
                winParams.flags |= bits;
            }
            window.setAttributes(winParams);
        }
    }

    public static void setStatusBarVisibleByDecorView(Activity activity, boolean visible) {
        Window window = activity.getWindow();
        if (window != null) {
            if (visible) {
                window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
            } else {
                window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN);
            }
        }
    }

    public static void setStatusBarTransparent(Activity activity) {
        Window window = activity.getWindow();
        if (window != null) {
            WindowManager.LayoutParams winParams = window.getAttributes();
            final int bits = WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
            winParams.flags |= bits;
            window.setAttributes(winParams);
        }
    }

    public static void setStatusBarTransparentDynamic(Activity activity, boolean overlay, boolean transparent) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Window window = activity.getWindow();
            if (window != null) {
                if (transparent) {
                    window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
                    if (!overlay) {
                        window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
                    }
                    window.setStatusBarColor(Color.TRANSPARENT);
                }
            }
        }
    }

    public static void setStatusBarBackgroundColor(Activity activity, int colorId) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            Window window = activity.getWindow();
            if (window != null) {
                window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
                window.setStatusBarColor(activity.getResources().getColor(colorId));
            }
        }
    }

    public static void setStatusBarOverlay(Activity activity, boolean overlay) {
        Window window = activity.getWindow();
        if (window != null) {
            if (overlay) {
                window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
            } else {
                window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
            }
        }
    }

    public static void setStatusBarItemColor(Activity activity, boolean dark) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            Window window = activity.getWindow();
            if (window != null) {
                final int bits = View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
                int currentBits = window.getDecorView().getSystemUiVisibility();
                if (dark) {
                    currentBits |= bits;
                } else {
                    currentBits &= ~bits;
                }
                window.getDecorView().setSystemUiVisibility(currentBits);
            }
        }
    }
}
複製程式碼

更多文章,歡迎訪問我的 Android 知識梳理系列:

相關文章