laitimes

Take notes on the road to the pit of the Android immersive status bar

author:Flash Gene

1- Preface

Regarding the name "immersive status bar", some friends may find it inappropriate. But at present, most of the "immersive status bar" on the Internet basically refers to the "transparent state bar", so I will not discuss its right or wrong here (in fact, sometimes there are many mistakes, and it becomes right), everyone knows that it is the "transparent state bar", and the following is called this effect "immersive state bar".

Before Android 4.4, all applications could not set the background color of the status bar, they all followed the system (black background status bar), and a black status bar was very incompatible with the application. In order to provide better interactivity, Google has provided a way to set up an immersive status bar after Android 4.4. The interface of the app that supports the immersive status bar seems to be a little higher, so the Android client also supported the immersive status bar at the beginning of the year. In the process of realizing the immersive status bar effect, I stepped on a lot of pitfalls, and I hereby record them. The following figure shows the comparison of the effect before and after setting the immersive status bar on the Android client of Notes:

Take notes on the road to the pit of the Android immersive status bar

Comparing the two effects, it is clear that the immersive status bar below looks a little more harmonious and beautiful.

2- How to implement an immersive status bar

2.1-Android 4.4 or later

Since the immersive status bar setting is only available after Android 4.4, we need to adapt to Android 4.4 and above. Android 4.4 has two ways to implement an immersive status bar, one is set in a resource file, and the other is set in code.

2.1.1 - Set up an immersive status bar in the resource file

First, we'll modify the values/styles.xml to add an empty style to it, inheriting from BaseTheme.

<resources>
<!-- Base application theme. --><style name="BaseTheme" 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>

<style name="AppTheme" parent="BaseTheme" />

</resources>           

Then add the following code to the styles.xml file in the values-v19 directory (if there is no one in the project, create a new one, the system above 4.4 will read the resource file in the directory):

<resources>
<style name="AppTheme" parent="BaseTheme"><item name="android:windowTranslucentStatus">true</item></style>

</resources>           

Then set the theme of the App to AppTheme. Note: The android:windowTranslucentStatus property was introduced in v19.

2.1.2 - Set in code

To make it easier to implement in code, we just need to add a FLAGTRANSLUCENTSTATUS flag to the BaseActivity.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
getWindow().addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);}           

After setting it up through the above two methods, the rendering is as follows:

Take notes on the road to the pit of the Android immersive status bar

We will find that just by setting the above settings the Toolbar will go to the status bar. Usually people think of using the fitsSystemWindows property to solve this problem.

fitSystemWindows官方描述:Boolean internal attribute to adjust view layout based on system windows such as the status bar. If true, adjusts the padding of this view to leave space for the system windows. Will only take effect if this view is in a non-embedded activity. 简单描述:这个属性的作用是让view可以根据系统窗口(如status bar)来调整自己的布局,如果值为true,就会调整view的paingding属性来给system windows留出空间(即给view添加一个值为状态栏高度的top padding)。

Let's try to set the fitsSystemWindows property to true for the Toolbar. The layout code is as follows:

<?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: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/my_toolbar"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:background="@color/colorPrimary"

android:minHeight="?attr/actionBarSize"

android:fitsSystemWindows="true"

android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" />

<FrameLayout

android:layout_width="match_parent"

android:layout_height="0dp"

android:layout_weight="1"

android:padding="16dp">

<RelativeLayout

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:background="#e0e0e0"

android:layout_gravity="bottom">

<EditText

android:layout_width="match_parent"

android:layout_height="40dp"

android:fitsSystemWindows="true"

android:background="@drawable/edit_text_rect_bg" />

</RelativeLayout>

</FrameLayout>

</LinearLayout>           

The above code is compared with Android 4.4 and Android 5.0+, and the above rendering is as follows:

Take notes on the road to the pit of the Android immersive status bar

As we can see from the comparison chart above, the status bar is fully transparent on Android 4.4 and semi-transparent on Android 5.0+.

Note: On some 4.4 systems, the status bar is not fully transparent, but rather gradient.

2.2-Android 5.0 or later

The effect of the immersive status bar has been implemented above, but if you run on a machine above Android 5.0, you will find that most mobile phones will have a status bar that is translucent.

There are also some apps above Android 5.0 that have this translucent effect of the status bar, such as QQ. However, some products and designs just want to unify the style, and all of them have a fully transparent status bar. Since Android 5.0, we have provided us with an API to set the color of the status bar, and we can set the color of the status bar by ourselves.

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
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);

}           

After adding the above code, and then run it on Android 5.0+ to see the effect, the status bar has become fully transparent, and the effect is the same as Android 4.4 in the picture above, and the picture is no longer attached here.

2.3-Android 6.0 or later, set the font color of the status bar

The default font color of the status bar on most mobile phones is white, and if the color of the toolbar or the interface head is lighter, then the white words on the status bar are not very clear. After Android 6.0, we can use the code to set the color of the status bar font to black, and the code is as follows:

window.getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);           

3- Stepped on pits

I thought that the immersive status bar had been perfectly realized above, but I didn't expect to find a series of pits during the test.

3.1 - The Toolbar is topped up when the soft keyboard pops up

If there is EditText or other input fields in the interface, you will find that when the software disk pops up, the contents of the Toolbar are all topped, as shown in the following figure:

Take notes on the road to the pit of the Android immersive status bar

Why is this? After research, it turned out to be the ghost of the fitsSystemWindows attribute. Which View is set to fitsSystemWindows=true, this View will be topped by the software disk. Therefore, fitsSystemWindows can't be used indiscriminately, there will be unexpected pitfalls. Can we do without fitsSystemWindows? Absolutely. As mentioned earlier, the function of fitsSystemWindows=true is to add padding to the status bar height to the View, so why don't we manually add padding to the Toolbar ourselves? Let's remove the fitsSystemWindows property on the Toolbar and set the padding of the Toolbar, as follows:

protected void setStatusBarPaddingAndHeight(View toolBar) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {if (toolBar != null) {int statusBarHeight = getSystemBarHeight(this);

toolBar.setPadding(toolBar.getPaddingLeft(), statusBarHeight, toolBar.getPaddingRight(),

toolBar.getPaddingBottom());

toolBar.getLayoutParams().height = statusBarHeight +

(int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 45, getResources().getDisplayMetrics());

}

}

}           

Remove the fitsSystemWindows property of the Toolbar, and add the above code, and the Toolbar is normal when the soft keyboard pops up. At present, the Android project uses code to add padding instead of fitsSystemWindows attributes.

3.2-When the soft keyboard pops up, the input fields such as EditText will be overwritten by the software disk

In the example diagram above where the software disk puts the Toolbar on top, we will also find a problem, that is, when the soft keyboard pops up, the EditText is not popped up but is covered by the soft keyboard.

It said above that after adding the fitsSystemWindows attribute to the Toolbar, it will be topped by the soft keyboard, so can we add a fitsSystemWindows attribute to the input box just to solve the problem of the input box being overwritten?

Take notes on the road to the pit of the Android immersive status bar

After trying it, I found that it worked, but the height of the input box changed, and it was actually the padding of the input box that increased the height of the status bar. Obviously, this is not a good way to solve it. Later found a workaround on stackoverflow: a solution to the FLAGTRANSLUCENTSTATUS causing the input box to be overwritten by a soft keyboard

We've tweaked it a bit, and the code looks like this:

public class AndroidBug5497Workaround {
public static void assistActivity(View content) {new AndroidBug5497Workaround(content);}

private View mChildOfContent;

private int usableHeightPrevious;

private ViewGroup.LayoutParams frameLayoutParams;

private AndroidBug5497Workaround(View content) {

if (content != null) {

mChildOfContent = content;

mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {

public void onGlobalLayout() {

possiblyResizeChildOfContent();

}

});

frameLayoutParams = mChildOfContent.getLayoutParams();

}

}

private void possiblyResizeChildOfContent() {

int usableHeightNow = computeUsableHeight();

if (usableHeightNow != usableHeightPrevious) {

//如果两次高度不一致

//将计算的可视高度设置成视图的高度

frameLayoutParams.height = usableHeightNow;

mChildOfContent.requestLayout();//请求重新布局

usableHeightPrevious = usableHeightNow;

}

}

private int computeUsableHeight() {

//计算视图可视高度

Rect r = new Rect();

mChildOfContent.getWindowVisibleDisplayFrame(r);

return r.bottom;

}

}           

添加上面的类,然后在Activity的onCreate方法中的setContentView后面加上如下代码:

AndroidBug5497Workaround.assistActivity(findViewById(android.R.id.content));           

Then run, the input box can be pushed up normally, and the layout of the input box is not affected.

Take notes on the road to the pit of the Android immersive status bar

The principle of this solution is to set a listener for the root layout of the interface, and when the size of the interface changes, such as when the keyboard pops up, reset the height of the root layout, and then call requestLayout to redraw the interface.

At present, Notes Android uses this solution, and so far I have not found any other problems caused by this solution.

3.3 - Pit on Huawei EMUI 3.1

Put the above immersive code on the EMUI3.1 system mobile phone (such as Huawei Honor 7) to run, you will find that there is no immersive effect at all, the status bar is transparent, and the color on the desktop is displayed, as shown in the following figure:

Take notes on the road to the pit of the Android immersive status bar

It has been verified that it is the reason for the EMUI3.1 system, and many apps also have immersive effects on EMUI3.0, but there is no effect when it comes to EMUI3.1. In EMUI3.1, there is no immersive effect, if it is the same as before 4.4, it is black, and it is okay, so the transparent display desktop color is really ugly. Later, it turned out that removing the following code can make it immersive.

window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);           

Here's what it looks like:

Take notes on the road to the pit of the Android immersive status bar

However, the status bar isn't fully transparent, but rather gradient like some 4.4 systems, but it's better than displaying desktop colors. Here we add a judgment to determine that if it is not an EMUI 3.1 system, we call clearFlags to clear the FLAGTRANSLUCENTSTATUS. The specific code is as follows:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
Window window = getWindow();window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

// 因为EMUI3.1系统与这种沉浸式方案API有点冲突,会没有沉浸式效果。

// 所以这里加了判断,EMUI3.1系统不清除FLAG_TRANSLUCENT_STATUS

if (!isEMUI3_1()) {

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);

}

}

public static boolean isEMUI3_1() {

if ("EmotionUI_3.1".equals(getEmuiVersion())) {

return true;

}

return false;

}

private static String getEmuiVersion(){

Class<?> classType = null;

try {

classType = Class.forName("android.os.SystemProperties");

Method getMethod = classType.getDeclaredMethod("get", String.class);

return (String)getMethod.invoke(classType, "ro.build.version.emui");

} catch (Exception e){

}

return "";

}           

3.4-CoordinatorLayout+AppBarLayout滚动隐藏导航栏遇到沉浸式状态栏的坑

This pit is mainly encountered when making financial headline needs.

Background: The headline function needs to implement two levels of TabLayout navigation, the first level is the Toolbar (headlines, products, and discovery), and the second level is the TabLayout for switching between various columns in the headlines. What needs to be achieved is that in the Headline Fragment, sliding the list of posts hides and shows the first-level navigation Toolbar. When the first-level navigation toolbar is displayed, swiping left and right is to switch between the first-level navigation tabs (i.e., headlines, discoveries, and products). When you scroll up and down the list of posts in the headline fragment to hide the first-level navigation toolbar, swipe left and right to switch the second-level navigation tabs (i.e., headline columns). The effect is shown in the figure below.
Take notes on the road to the pit of the Android immersive status bar

Scrolling lists to hide and show Toolbar, the first thing that definitely comes to mind is CoordinatorLayout+AppBarLayout. Based on the immersive effects that have been implemented in the project, add and modify the layout in the activity:

<android.support.design.widget.CoordinatorLayout
android:id="@+id/coordinator_layout"android:layout_height="match_parent"android:layout_width="match_parent">

<android.support.design.widget.AppBarLayout

android:id="@+id/appbar_layout"

android:layout_width="match_parent"

android:layout_height="wrap_content"

app:elevation="0dp">

<android.support.v7.widget.Toolbar

android:layout_width="match_parent"

android:layout_height="?attr/actionBarSize"

app:contentInsetStart="0dip"

app:contentInsetLeft="0dip"

app:contentInsetEnd="0dip"

app:layout_scrollFlags="scroll|enterAlways">

...部分代码省略...

<android.support.design.widget.TabLayout

android:id="@+id/tab_layout"

android:layout_width="wrap_content"

android:layout_height="match_parent"

android:layout_centerHorizontal="true"

app:tabBackground="@null"

app:tabIndicatorColor="@color/tab_text_selected_color"

app:tabIndicatorHeight="2dip"

app:tabMode="fixed"

app:tabGravity="fill"

app:tabPaddingStart="14dp"

app:tabPaddingEnd="14dp"

app:tabTextAppearance="@style/FinanceTabTextAppearance"

app:tabSelectedTextColor="@color/tab_text_selected_color"

app:tabTextColor="@color/tab_text_unselected_color" />

</android.support.v7.widget.Toolbar>

</android.support.design.widget.AppBarLayout>

<android.support.v4.view.ViewPager

android:id="@+id/pager"

android:layout_width="match_parent"

android:paddingBottom="50dp"

android:layout_height="match_parent"

android:overScrollMode="never"

app:layout_behavior="@string/appbar_scrolling_view_behavior"/>

</android.support.design.widget.CoordinatorLayout>           

Layout is a tab that adds a TabLayout to the Toolbar as a first-level navigation. Then use a ViewPager and add three fragments to the ViewPager, which are the headline, the product, and the discovered fragment. Among them, TabLayout and ViewPager are nested in the headline fragment. Based on the immersive implementation, add a status bar height padding to AppBarLayout in the code. I thought it would be done, but it turned out that after running, after sliding up to hide AppBarLayout, and then pulling down, it will exceed the drop-down range, that is, there will be an extra blank of the height of the status bar when dropping down, and the effect is at the top of the figure below:

Take notes on the road to the pit of the Android immersive status bar

After continuous trial and exploration, I found that you can add the following flag to the activity:

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

Well, not bad, the sliding problem solved. But I was always uneasy in my heart, and I always felt that there was a pit. Later, I found that there was indeed a pit,After adding this flag,Some Huawei mobile phones with virtual buttons have the problem of virtual buttons blocking the bottom layout,It has been verified that only EMUI3.1 has this problem (it's EMUI3.1 again,It's powerless to complain)。 In the end, after all the twists and turns, I finally found a way to effectively solve the sliding problem after CoordinatorLayout + AppBarLayout and set paddingtop to AppBarLayout.

this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
this.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);           

I thought that the above solution was perfect without any problems, but I didn't expect that there were still pitfalls. Soon after, the test found a problem on the live network: when the input box in the WebView gets focus, the soft keyboard pops up, and when the interface is exited, a black block the size of the soft keyboard appears at the bottom layout. As shown in the figure below:

Take notes on the road to the pit of the Android immersive status bar

After troubleshooting, this problem is caused by the above code. There is no choice but to remove the above code and find another solution to deal with the sliding problem of CoordinatorLayout + AppBarLayout and paddingtop for AppBarLayout. Later, I found that adding the following code to the onCreate method of the activity can solve this problem perfectly.

if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.root_container_layout), new android.support.v4.view.OnApplyWindowInsetsListener() {@Overridepublic WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
return insets.consumeSystemWindowInsets();
}
});
}           

4- Summary

The above is a handwritten note of the pitfalls encountered in the implementation of the immersive status bar in the Android project and the solutions. Finally, after Android realizes the status bar effect, the above renderings are as follows:

Take notes on the road to the pit of the Android immersive status bar

After the development of the immersive status bar, it was found that there are several pits that are easy to step on: 1. fitsSystemWindows=true should be used with caution, there are many pits. For example, when the input box in the WebView gets the focus and pops up the soft keyboard, which view is set to fitsSystemWindows=true, which view will be topped when the soft keyboard pops up; 2.WindowManager.LayoutParams.FLAGLAYOUTNO_LIMITS do not use it, it will cause the virtual keys under the EMUI3.1 system to block the layout;

5- Reference documentation

https://stackoverflow.com/questions/7417123/android-how-to-adjust-layout-in-full-screen-mode-when-softkeyboard-is-visible

Author: Liu Ling

Source-WeChat public account: Suishouji technical team

Source: https://mp.weixin.qq.com/s/d6D_rYmzlLxJrSDAN1WByA