1. 程式人生 > >Android自定義狀態列顏色

Android自定義狀態列顏色

本篇部落格參考:

在日常的開發中,為了保證App風格的統一,很多時候我們需要自定義狀態列的顏色。其實裡面的坑還蠻多的,因為涉及到版本的相容性。好了,廢話不多說,開始今天的正題。

我們今天的效果是要做成QQ那樣的效果,如圖(MX2,Android4.4.4):
這裡寫圖片描述

想要實現這種效果其實並不難,但是由於Android版本眾多,各版本API並不能很好的相容,造成實現過程可能會有一些bug,但是,不怕,有博主在,坑都踩的差不多了。

方式一:通過設定不同的styles,並在程式碼裡判斷API等級來動態設定狀態列顏色(相容到Android 4.4,API19以上)

預設效果如下:
這裡寫圖片描述

我們最終的效果是把它的顏色設定成QQ狀態列的顏色。根據我有限的經驗,貌似Android 4.3 API18(包括)以下是不可以修改狀態列顏色的,而Android 5.0 API21以後是可以直接在程式碼或者styles裡面設定狀態列顏色的。所以我們的實現就分兩種情況,一種是Android4.4(API 19)~Android4.4(API 20),另一種是Android5.0(API 21)以上。

在 values/styles 裡設定主題:(注意繼承的為一個相容主題,之前用的 android:Theme.Holo.Light.NoActionBar 不好用)

<style name="MyAppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    ...
</style>

在 values-v19 裡設定主題:

<style name="MyAppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:windowTranslucentStatus"
>true</item> </style>

在 values-v21 裡設定主題:(我們這裡只需要修改statusBarColor為需要的顏色就可以了,這種是在檔案中寫死了,後面還會講API21以後怎麼在程式碼裡動態設定狀態列的顏色)

<style name="MyAppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:colorPrimaryDark">@color/primary_black</item>  //這個用來設定頂部狀態列和底部導航欄的顏色
<item name="android:statusBarColor">@color/primary_black</item> //這個用來設定頂部狀態列顏色 <item name="android:navigationBarColor">@color/green_5a</item> //這個用來設定底部導航欄顏色 </style>

這時候執行效果如下:
這裡寫圖片描述

我們發現佈局竟然都頂到狀態列去了,這是由於 API 19 的主題裡設定的狀態列為透明的,系統預設它將不佔用空間,導致你自己寫的佈局會頂上去,所以還需要在程式碼裡額外設定狀態列的顏色和佈局的排版。先看一下之前錯誤的程式碼,注意這段程式碼必須寫在 setContentView 以後(比如可以寫在 BaseActivity 的 onStart() 中):

if(Build.VERSION.SDK_INT >= 19 && Build.VERSION.SDK_INT < 21) {
    ViewGroup contentView = (ViewGroup) findViewById(Window.ID_ANDROID_CONTENT);
    if(contentView.getChildAt(0) != null) {
        contentView.getChildAt(0).setFitsSystemWindows(true); //fitSystemWindow:標識佈局調整時是否考慮系統視窗(如狀態列)
        contentView.getChildAt(0).setBackgroundColor(getResources().getColor(R.color.primary_black));
    }
}

執行效果如下:
這裡寫圖片描述

我們這裡是先拿到整個佈局,然後拿到它的第一個子孩子,即你XML檔案裡寫的根佈局,然後設定它的屬性。這裡錯就錯在設定成了根佈局的整體顏色,這就造成了你所有的佈局都有了統一的背景。
修改後的程式碼:

@Override
protected void onStart() {
    super.onStart();
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
        ViewGroup contentView = (ViewGroup) findViewById(android.R.id.content);
        View statusBarView = new View(this);
        ViewGroup.LayoutParams lp = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, getStatusBarHeight(this));
        statusBarView.setBackgroundColor(getResources().getColor(R.color.primary_black));
        contentView.getChildAt(0).setFitsSystemWindows(true);
        contentView.addView(statusBarView, lp);
    }
}

public static int getStatusBarHeight(Context context) {
    int result = 0;
    int resourceId = context.getResources().getIdentifier("status_bar_height", "dimen", "android");
    if (resourceId > 0) {
        result = context.getResources().getDimensionPixelSize(resourceId);
    }
    return result;
}

通過拿到狀態列的高度,只單獨設定它的顏色,對根佈局沒有影響。最後的效果如下:
這裡寫圖片描述

另外,為了能夠像餓了麼那樣的效果,就是把狀態列設定成透明色,不過這個效果只有在5.0以後才可以,實現程式碼如下(再說一遍,必須在setContentView之後!):

if (Build.VERSION.SDK_INT >= 21) {
    View decorView = getWindow().getDecorView();
    int option = View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE;
    decorView.setSystemUiVisibility(option);
    getWindow().setStatusBarColor(Color.TRANSPARENT);   //這裡可以動態設定狀態列的顏色
}
//如果主題設定的是NoActionBar,就不需要下面的程式碼了
ActionBar actionBar = getSupportActionBar();
actionBar.hide();

效果如下:
這裡寫圖片描述

方式二:先把狀態列設定成透明的,然後設定屬性和背景
在程式碼裡或者styles裡配置,讓狀態列變透明:

在程式碼裡設定:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);

或者在styles裡對應Activity的theme裡新增:
<item name="android:windowTranslucentStatus">true</item>

在程式碼或者根佈局裡配置:

在setContentView後設置:
ViewGroup contentView = (ViewGroup) findViewById(android.R.id.content);
ViewGroup childView = (ViewGroup) contentView.getChildAt(0);
childView.setFitsSystemWindows(true);
childView.setClipToPadding(true);
childView.setBackgroundColor(getResources().getColor(R.color.primaryOrange_f6));

或者在根佈局裡設定:
android:background="@color/color_qq_blue"
android:fitsSystemWindows="true"
android:clipToPadding="true"

執行後效果如下:
這裡寫圖片描述

這種還是把整個根佈局設定成了固定的顏色,和上面的錯誤一樣,在寫的時候還要再套一層佈局,類似下面的程式碼:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <!--在根佈局下再巢狀一層佈局-->
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/white">  //這裡設定的是狀態列下面你自己應用介面的背景
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:textColor="@android:color/black"
            android:text="Hello World!"/>
    </LinearLayout>
</LinearLayout>

綜上:方式二適用於專案剛開始,或者還沒有多少個頁面的時候,因為用這種寫法需要把每個xml佈局都內嵌一層,不推薦使用。而方式一可以只拿到狀態列高度後單獨設定狀態列的顏色,對根佈局沒有影響,我現在專案中用的也是方式一。