Immersive status bar effect

As we can see, the immersive state is very beautiful.

What is an immersive status bar

1. Transparent status bar

2. The position in the status bar displays our custom color, usually the color of the Toolbar or ActionBar
Or ** take the entire picture ** and put it in the status bar.

Before we learn about immersive status bars, let’s look at the property fitSystemWindows, which is very useful for implementing immersive status bars.

fitSystemWindows

What is fitSystemWindow?

Set the Window Status of the system, such as the Status bar

Set to true: Will adjust the View padding to give the system window ** room to display the Status bar
If set to false: do not give the system window outflow control, do not display Status bar**

example

<? The XML version = "1.0" encoding = "utf-8"? > <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" > <android.support.v7.widget.Toolbar android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#ff0000" /> </RelativeLayout>Copy the code

The displayed effect is:

If set to android: fitsSystemWindows = “false”, the layout is as follows

<? The XML version = "1.0" encoding = "utf-8"? > <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="false" > <android.support.v7.widget.Toolbar android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#ff0000" /> </RelativeLayout>Copy the code

Effect:

thinking

Why doesn’t it work? FitSystemWindow will affect the display or shadow of the Status bar in Windows.

why

This property must be combined with the transparent status bar to be effective

It automatically adds a value equal to the PaddingTop of the status bar height to the View.

So let’s actually do it

Step 1: Transparent status bar implementation

Here we offer two options

The first approach: implement it in a topic

Since the modified status bar did not appear until **4.4 **, when we create the theme file we need to create ** corresponding ** package
1. Create the **values-v19** folder, then create the style. XML file, and define the theme AppTheme
<? The XML version = "1.0" encoding = "utf-8"? > <resources> <style name="AppTheme.Main" parent="Theme.AppCompat.Light.NoActionBar"> <! -- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> <item name="android:windowTranslucentStatus">true</item> </style> </resources>Copy the code

2. Then let MainActivity inherit the theme

The second option: control in code
package com.example.cdx.test;

import android.os.Build;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.WindowManager;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //>18版本就能进行设置了
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            //透明状态栏
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
        }
    }
}
Copy the code

Step 2: Then use TextView in the layout file

<? The XML version = "1.0" encoding = "utf-8"? > <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:text="Love You" android:textSize="20sp"  android:background="@android:color/holo_blue_bright" />Copy the code

Operation effect:

I found the text overlapped with the status bar.

Step 3: add android: fitsSystemWindows = “true” attribute

<? The XML version = "1.0" encoding = "utf-8"? > <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:text="Love You" android:textSize="20sp" android:background="@android:color/holo_blue_bright" />Copy the code

rendering

Please see the requirements below

Use it on the Toolbar

The above example is in the root layout that is used on the TextView fitSystemWindow on using the android: this property fitsSystemWindows = “true”, but in the actual development, we are more common is the situation

Step 1: Create the values-v19 folder, then create the style.xml file

<? The XML version = "1.0" encoding = "utf-8"? > <resources> <style name="AppTheme.Main" parent="Theme.AppCompat.Light.NoActionBar"> <! -- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> <item name="android:windowTranslucentStatus">true</item> </style> </resources>Copy the code

Step 2: Then let MainActivity inherit the theme

<activity android:name=".MainActivity"
            android:theme="@style/AppTheme.Main"
            >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
Copy the code

Step 3: then I’ll demonstrate the error by writing fitSystemWindow on a LinearLayout

<? The XML version = "1.0" encoding = "utf-8"? > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:orientation="vertical" > <android.support.v7.widget.Toolbar android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/holo_blue_light" > <TextView Android :layout_height="wrap_content" Android :layout_height="wrap_content" android:layout_gravity="center" /> </android.support.v7.widget.Toolbar> </LinearLayout>Copy the code

Run, the effect is as follows:

Step 4: I have modified the third step, the android: fitsSystemWindows = “true” uninstall the Toolbar
So let’s run it and see what it looks like. Very nice

Next I’ll implement the transparent status bar in code

Step 1: Introduce the Toolbar in the layout file. Note that it does not say fitSystemWindow

<? The XML version = "1.0" encoding = "utf-8"? > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/holo_blue_light" > <TextView Android :layout_height="wrap_content" Android :layout_height="wrap_content" android:layout_gravity="center" /> </android.support.v7.widget.Toolbar> </LinearLayout>Copy the code

Step 2: Implement the status bar in MainActivity

package com.example.cdx.test; import android.content.Context; import android.os.Build; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.support.v7.widget.Toolbar; import android.view.View; import android.view.Window; import android.view.WindowManager; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); ActionBar actionBar = getSupportActionBar(); if (actionBar! =null){ actionBar.setDisplayShowTitleEnabled(false); } // Set the Window's representation to a transparent status bar identifier // Step 3: Set the Toolbar padding to the height of the status bar If (build.version.sdk_int >= build.version_codes.lollipop) {setImmerseLayout(toolbar); }} protected void setImmerseLayout(View View) {// if (build.version.sdk_int >= Build.VERSION_CODES.KITKAT) { Window window = getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS); int statusBarHeight = getStatusBarHeight(this.getBaseContext()); view.setPadding(0, statusBarHeight, 0, 0); } } public 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; }}Copy the code

The renderings are perfect.

Alternative implementation

Today we see another way to achieve this effect in a project

Let’s see how that works.

First look at this graph where the color of the status bar is controlled by colorPrimaryDark

so

Step 1: We can control the color of colorPrimaryDark in the AppTheme

<resources> <! -- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <! -- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@android:color/holo_blue_light</item> <item name="colorAccent">@color/colorAccent</item> </style> </resources>Copy the code

Step 2: Then let MainActivity inherit the theme

Step 3: Make the toolbar color in the layout file the same as the colorPrimaryDark color in the theme

<? The XML version = "1.0" encoding = "utf-8"? > <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:fitsSystemWindows="true" android:orientation="vertical" > <android.support.v7.widget.Toolbar android:id="@+id/login_toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@android:color/holo_blue_light" app:titleTextColor="@color/skin_toolbar_title" /> </RelativeLayout>Copy the code

Run, the effect is as follows:

Immersive status bar implementation

The first way

Note: The first way is not comprehensive. After reading the first way, please continue to see the first way supplement.

rendering

Implementation approach

1, use NoActionBar themes. AppCompat. Light. NoActionBar, used to hide the title bar

2, use transparent status bar true,

3. Let the control have a padding above the top.

The specific implementation

Step 1: Set the theme and set the transparent status bar

<? The XML version = "1.0" encoding = "utf-8"? > <resources> <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <! -- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> <! - transparent status bar - > < item name = "android: windowTranslucentStatus" > true < / item > < / style > < / resources >Copy the code

2. Then let the Application use the theme

android:theme="@style/AppTheme"
Copy the code

3, use the android: fitsSystemWindows = “true” let TextView distance in the status bar at the top of the have a padding length for the status bar height.

Note: the android: fitsSystemWindows = “true” to set up on the TextView controls, do not set on the LinearLayout controls.

<? The XML version = "1.0" encoding = "utf-8"? > <LinearLayout 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=".MainActivity" android:orientation="horizontal" > <TextView android:layout_width="match_parent" android:layout_height="60dp" android:text="Hello World!" android:background="@color/colorAccent" android:fitsSystemWindows="true" /> </LinearLayout>Copy the code

A supplement to the first approach: compatibility

Background:

1. Before Android4.4, when we opened an App, we would always see the black notification bar at the top of the system, which would make the App look more abrupt and unattractive.

2. After Android4.4, always introduces the feature ** always System Bar**
3. Mimics some of the UI effects of IOS

Imperfections:

Status bar transparency is a new feature since android4.4. It wasn’t previously available on android4.4, so it’s important to make sure that you’re version-specific.

Android 4.4 below: is an implementation logic.

Android4.4 – Android5.0 below: is an implementation logic.

Android5.0 and above: is an implementation logic.

How to do it:

Step 1: Set AppTheme. AppTheme is the style of NoActionBar.

<! -- Base application theme. --> <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar"> <! -- Customize your theme here. --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> </style>Copy the code

Step 2: You need to set the Theme in the style. XML of values,values-v19,values-v21.

values/style.xml

<style name="ImageTranslucentTheme" parent="AppTheme"> <! < span style = "box-sizing: border-box! Important; word-wrap: break-word! Important;Copy the code

values-v19/style.xml

<style name="ImageTranslucentTheme" parent="AppTheme"> <! - whether the status bar transparency: transparent - > < item name = "android: windowTranslucentStatus" > true < / item > <! - whether the navigation bar transparency: transparent - > < item name = "android: windowTranslucentNavigation" > true < / item > < / style >Copy the code

values-v21/style.xml

<style name="ImageTranslucentTheme" parent="AppTheme"> <! - whether the status bar transparency: transparent - > < item name = "android: windowTranslucentStatus" > false < / item > <! - whether the navigation bar transparency: transparent - > < item name = "android: windowTranslucentNavigation" > true < / item > <! --Android 5.x starts by setting the color to transparent, Or the navigation bar will present the light grey system default - > < item name = "android: statusBarColor" > @ android: color/transparent < / item > < / style >Copy the code

The first way to add: set the status bar text color

Requirements:

There’s often a need to set up an immersive status bar, but the product wants to set the text color status on the status bar,

We can make the text on the status bar darker by setting the status bar to light mode.

How to do it:

Step 1: Introduce the utility classes

package com.example.myapplication;

import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.os.Build;
import android.util.DisplayMetrics;
import android.view.Display;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;

import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class StatusBarUtil {
  /**
   * 修改状态栏为全透明
   */
  public static void transparencyBar(Activity activity) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
      Window window = activity.getWindow();
      window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
      window.getDecorView()
          .setSystemUiVisibility(
              View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
      window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
      window.setStatusBarColor(Color.TRANSPARENT);
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
      Window window = activity.getWindow();
      window.setFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS,
          WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);
    }
  }

  /**
   * 状态栏亮色模式,设置状态栏黑色文字、图标,
   * 适配4.4以上版本MIUIV、Flyme和6.0以上版本其他Android
   *
   * @return 1:MIUUI 2:Flyme 3:android6.0
   */
  public static void statusBarLightMode(Activity activity) {
    int result = 0;
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
      if (miuiSetStatusBarLightMode(activity, true)) {
        result = 1;
      } else if (flymeSetStatusBarLightMode(activity.getWindow(), true)) {
        result = 2;
      } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        activity.getWindow()
            .getDecorView()
            .setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
        result = 3;
      }
    }
  }

  /**
   * 已知系统类型时,设置状态栏黑色文字、图标。
   * 适配4.4以上版本MIUIV、Flyme和6.0以上版本其他Android
   *
   * @param type 1:MIUUI 2:Flyme 3:android6.0
   */
  public static void statusBarLightMode(Activity activity, int type) {
    if (type == 1) {
      miuiSetStatusBarLightMode(activity, true);
    } else if (type == 2) {
      flymeSetStatusBarLightMode(activity.getWindow(), true);
    } else if (type == 3) {
      activity.getWindow()
          .getDecorView()
          .setSystemUiVisibility(
              View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
    }
  }

  /**
   * 状态栏暗色模式,清除MIUI、flyme或6.0以上版本状态栏黑色文字、图标
   */
  public static void StatusBarDarkMode(Activity activity, int type) {
    if (type == 1) {
      miuiSetStatusBarLightMode(activity, false);
    } else if (type == 2) {
      flymeSetStatusBarLightMode(activity.getWindow(), false);
    } else if (type == 3) {
      activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
    }
  }

  /**
   * 设置状态栏图标为深色和魅族特定的文字风格
   * 可以用来判断是否为Flyme用户
   *
   * @param window 需要设置的窗口
   * @param dark 是否把状态栏文字及图标颜色设置为深色
   * @return boolean 成功执行返回true
   */
  public static boolean flymeSetStatusBarLightMode(Window window, boolean dark) {
    boolean result = false;
    if (window != null) {
      try {
        WindowManager.LayoutParams lp = window.getAttributes();
        Field darkFlag =
            WindowManager.LayoutParams.class.getDeclaredField("MEIZU_FLAG_DARK_STATUS_BAR_ICON");
        Field meizuFlags = WindowManager.LayoutParams.class.getDeclaredField("meizuFlags");
        darkFlag.setAccessible(true);
        meizuFlags.setAccessible(true);
        int bit = darkFlag.getInt(null);
        int value = meizuFlags.getInt(lp);
        if (dark) {
          value |= bit;
        } else {
          value &= ~bit;
        }
        meizuFlags.setInt(lp, value);
        window.setAttributes(lp);
        result = true;
      } catch (Exception e) {

      }
    }
    return result;
  }

  /**
   * 需要MIUIV6以上
   *
   * @param dark 是否把状态栏文字及图标颜色设置为深色
   * @return boolean 成功执行返回true
   */
  public static boolean miuiSetStatusBarLightMode(Activity activity, boolean dark) {
    boolean result = false;
    Window window = activity.getWindow();
    if (window != null) {
      Class clazz = window.getClass();
      try {
        int darkModeFlag;
        Class layoutParams = Class.forName("android.view.MiuiWindowManager$LayoutParams");
        Field field = layoutParams.getField("EXTRA_FLAG_STATUS_BAR_DARK_MODE");
        darkModeFlag = field.getInt(layoutParams);
        //noinspection unchecked
        Method extraFlagField = clazz.getMethod("setExtraFlags", int.class, int.class);
        if (dark) {
          //状态栏透明且黑色字体
          extraFlagField.invoke(window, darkModeFlag, darkModeFlag);
        } else {
          //清除黑色字体
          extraFlagField.invoke(window, 0, darkModeFlag);
        }
        result = true;

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
          //开发版 7.7.13 及以后版本采用了系统API,旧方法无效但不会报错,所以两个方式都要加上
          if (dark) {
            activity.getWindow()
                .getDecorView()
                .setSystemUiVisibility(
                    View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
          } else {
            activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
          }
        }
      } catch (Exception e) {

      }
    }
    return result;
  }

  //获取屏幕虚拟键高度
  public static int getVirtualBarHeight(Context context) {
    int vh = 0;
    WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    Display display = windowManager.getDefaultDisplay();
    DisplayMetrics dm = new DisplayMetrics();
    try {
      @SuppressWarnings("rawtypes") Class c = Class.forName("android.view.Display");
      @SuppressWarnings("unchecked") Method method =
          c.getMethod("getRealMetrics", DisplayMetrics.class);
      method.invoke(display, dm);
      vh = dm.heightPixels - display.getHeight();
    } catch (Exception e) {
      e.printStackTrace();
    }
    return vh;
  }

  public static int getStatusBarHeight(Context context) {
    int height = 0;
    try {
      Class c = Class.forName("com.android.internal.R$dimen");
      Field f = c.getField("status_bar_height");
      int id = (int) f.get(null);
      height = context.getResources().getDimensionPixelSize(id);
    } catch (Exception e) {
    }
    return height;
  }

  public static int dip2px(Context context, float dpValue) {
    final float scale = context.getResources().getDisplayMetrics().density;
    return (int) (dpValue * scale + 0.5f);
  }

  public static int px2dip(Context context, float pxValue) {
    final float scale = context.getResources().getDisplayMetrics().density;
    return (int) (pxValue / scale + 0.5f);
  }

  /** 获取手机的密度 */
  public static float getDensity(Context context) {
    DisplayMetrics dm = context.getResources().getDisplayMetrics();
    return dm.density;
  }
}
Copy the code

The second step: then set before the setContentView StatusBarUtil statusBarLightMode (this);

package com.example.myapplication; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); StatusBarUtil.statusBarLightMode(this); setContentView(R.layout.activity_main); }}Copy the code

The second realization: the ultimate solution

First, let’s look at the status bar and navigation bar. There are four main effects:

1. Customize the color of the status bar and navigation bar
2. Translucent status bar and navigation bar
3. Completely transparent status bar and navigation bar (actually the limit of the second, which I prefer to call immersive status bar and navigation bar)
4. Hide the status bar and navigation bar

How to implement

To do this, use the UltimateBar (** UltimateBar **) library, which is available for Android4.4 and Android5.0
So that he in different versions of the effect to achieve unprecedented unity, very good.
It’s on Github, and it’s pretty good.