I believe that most developers who use android:fitsSystemWindows are basically to achieve the immersive status bar effect.
Here I will first explain what is the immersive status bar effect.
The bar at the top of an Android device that displays various notifications and status information is called the status bar.

Usually, the content of our application is displayed below the status bar. But sometimes in order to achieve better visual effects, we want to extend the content of the application behind the status bar, which can be called an immersive status bar.

So how to achieve the immersive mode with the help of the android:fitsSystemWindows
attribute?
Why sometimes this attribute doesn’t working? let’s reveal step by step.
Maybe your understanding is that the android:fitsSystemWindows
attribute like a switch. True turn on the immersive mode and False turn off the immersive mode?
but it’s not
Let’s demonstrate it with a example.
First of all, in order to enter immersive mode, you need to change the system status bar to transparent, the code is as follows:
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) window.statusBarColor = Color.TRANSPARENT } }
Next, we add the android:fitsSystemWindows
attribute to the root layout of activity_main.xml, and set a background color for the layout:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ff66ff"
android:fitsSystemWindows="true">
</FrameLayout>
Code language: HTML, XML (xml)
Run the code, the effect is as shown below:

From the background color of the layout, we can see that the content of the layout does not extend behind the system status bar. That is, even though the android:fitsSystemWindows
property is set, we still can not enter the immersive mode
But don’t worry, we just need to make a small modification:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ff66ff"
android:fitsSystemWindows="true">
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Code language: HTML, XML (xml)
As you can see, the root layout is only modified from FrameLayout
to CoordinatorLayout
, nothing else changed. Then restart the program. Results as shown below:

In this way, the immersive mode can be successfully achieved.
In other words, why does the android:fitsSystemWindows
take effect when set on the CoordinatorLayout
layout, but not on the FrameLayout
layout?
This is because the configuration in xml is just a tag after all. If you want to produce specific effects in the application, it depends on how the tags are processed in the code.
Obviously, FrameLayout
does not handle the android:fitsSystemWindows
, so it will not change whether it is set or not.
The CoordinatorLayout
is different, we can dive into its source code:
private void setupForInsets() {
if (Build.VERSION.SDK_INT < 21) {
return;
}
if (ViewCompat.getFitsSystemWindows(this)) {
if (mApplyWindowInsetsListener == null) {
mApplyWindowInsetsListener =
new androidx.core.view.OnApplyWindowInsetsListener() {
@Override
public WindowInsetsCompat onApplyWindowInsets(View v,
WindowInsetsCompat insets) {
return setWindowInsets(insets);
}
};
}
// First apply the insets listener
ViewCompat.setOnApplyWindowInsetsListener(this, mApplyWindowInsetsListener);
// Now set the sys ui flags to enable us to lay out in the window insets
setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
} else {
ViewCompat.setOnApplyWindowInsetsListener(this, null);
}
}
Code language: JavaScript (javascript)
You can see that when set the android:fitsSystemWindows
, CoordinatorLayout
will do some processing on the insets of the current layout, and call the following line of code:
setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
This line of code is the crux of everything. To be precise, it is because of the execution of this line of code that we can extend the content of the layout to the system status bar area.
In fact CoordinatorLayout
does much more than that.
Because the immersive mode actually brings a lot of problems. Let the content of the layout extend behind the status bar. What if some interactive controls are obscured by the status bar? This way these controls can not be clicked and interacted with.
In order to solve this problem, CoordinatorLayout
will offset all internal child Views to a certain extent to ensure that they will not be blocked by the status bar.
For example, we add another button to the CoordinatorLayout:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ff66ff"
android:fitsSystemWindows="true">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Code language: HTML, XML (xml)
Run the program, the effect is as shown below:

You can seen that although the CoordinatorLayout
extends to the status bar area, the buttons it contains will not enter the status bar area, thus avoiding the situation where the interactive controls are occluded.
But what if I just want some child controls to extend into the status bar area? For example, if I put a picture in the CoordinatorLayout
, according to this rule, the picture will not be displayed behind the status bar, so the desired effect will not be achieved.
We can try this scenario. For example, add another ImageView to CoordinatorLayout:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ff66ff"
android:fitsSystemWindows="true">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/bg"
/>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Code language: HTML, XML (xml)
Now run the program, the effect is as shown below:

Indeed, the image does not enter the status bar area, consistent with the theory we explained earlier.
But obviously, this is not the effect we want, so what can be done?
Here we can achieve this with the help of other layouts. Among the many layouts provided by Google, not only CoordinatorLayout
can handle the android:fitsSystemWindows
property, CollapsingToolbarLayout
and DrawerLayout
will also handle this property.
Now modify activity_main.xml as follows:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ff66ff"
android:fitsSystemWindows="true">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/bg"
android:fitsSystemWindows="true"
/>
</com.google.android.material.appbar.CollapsingToolbarLayout>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
Code language: HTML, XML (xml)
As you can see, here we wrap another layer of CollapsingToolbarLayout
outside the ImageView
, and also set android:fitsSystemWindows
property for CollapsingToolbarLayout
, so that CollapsingToolbarLayout
can extend the content to the status bar area.
Then we also set the android:fitsSystemWindows
property to ImageView
, so that the image can be displayed behind the status bar.
Re-run the program, the effect is as shown below:

It worth mention that CollapsingToolbarLayout
must be used in conjunction with CoordinatorLayout
, not alone. Because CollapsingToolbarLayout
only adjusts the offset distance of the internal controls, and does not call setSystemUiVisibility()
function like CoordinatorLayout to open the immersive status bar.
Seeing this, I believe everyone already knows how to achieve the immersive status bar effect.
But sometimes due to the project limitations, you cannot use CoordinatorLayout
or CollapsingToolbarLayout
, but can only use traditional layouts like FrameLayout
or LinearLayout
. then what to do?
In fact, after we know the principle of CoordinatorLayout
handle the immersive mode, we naturally know how to implement it manually, because the essence is to call the setSystemUiVisibility()
function.
Now we change activity_main.xml to use the traditional FrameLayout layout:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/root_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#ff66ff">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/bg"
/>
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
/>
</FrameLayout>
Code language: HTML, XML (xml)
In order to achieve the effect of the immersive status bar, we manually call the setSystemUiVisibility()
function in MainActivity
to extend the content of the FrameLayout to the status bar area:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
window.statusBarColor = Color.TRANSPARENT
val frameLayout = findViewById<FrameLayout>(R.id.root_layout)
frameLayout.systemUiVisibility = (SYSTEM_UI_FLAG_LAYOUT_STABLE
or SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
}
}
Code language: HTML, XML (xml)
Be aware that the setSystemUiVisibility()
function has actually deprecated. Starting with Android 11, Google provides a new API WindowInsetsController
to achieve the same feature
Now re-run the program, the effect is as shown below:

As you can see, we still achieve the effect of the immersive status bar, but the problem is that the buttons in the FrameLayout also extend to the status bar area, which is the problem that the interactive controls are obscured by the status bar mentioned above.
The reason for this problem is also easy to understand, because we used CoordinatorLayout before, which has helped us take these things into consideration and automatically offset the internal controls. And now FrameLayout obviously won’t do these things for us, so we have to figure out a way to solve it ourselves.
In fact, we can use the setOnApplyWindowInsetsListener()
function to monitor the event that WindowInsets changes. When there is a change in the monitor, we can read the size of the top Insets, and then offset the control by the corresponding distance.
Modify the code in MainActivity as follows:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
window.statusBarColor = Color.TRANSPARENT
val frameLayout = findViewById<FrameLayout>(R.id.root_layout)
frameLayout.systemUiVisibility = (SYSTEM_UI_FLAG_LAYOUT_STABLE
or SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
val button = findViewById<Button>(R.id.button)
ViewCompat.setOnApplyWindowInsetsListener(button) { view, insets ->
val params = view.layoutParams as FrameLayout.LayoutParams
params.topMargin = insets.systemWindowInsetTop
insets
}
}
}
Code language: HTML, XML (xml)
You can see that when we monitor the change of WindowInsets, we can call systemWindowInsetTop
to get the height of the status bar, and then make corresponding offsets for the controls that do not need to extend to the status bar area.
Re-run the program, the effect is as shown below:
