What Are Windowinsets

What are WindowInsets?

You can learn all about WindowInsets here. WindowInsets provides you with the area on the window that is usable by the application. By itself it's of not much use. It's true purpose comes when you either override View.onApplyWindowInsets or implement View.OnApplyWindowInsetsListener. You can read about them here: View.onApplyWindowInsets and View.OnApplyWindowInsetsListener.

Listener for applying window insets on a view in a custom way.

Apps may choose to implement this interface if they want to apply
custom policy to the way that window insets are treated for a view. If
an OnApplyWindowInsetsListener is set, its onApplyWindowInsets method
will be called instead of the View's own onApplyWindowInsets method.
The listener may optionally call the parameter View's
onApplyWindowInsets method to apply the View's normal behavior as part
of its own.

In short, overriding this will let you control area of the window available for your View.

In Android, what are window insets?

They are some kind of colored margin (used in Android Wear).

They are used to create a padding from the main content to the actual border:

There are a few examples here.


This is an image with 2 insets: Circle/Squared.

Sample Image


They can also be used in other views to handle especial rendering requirements, like in a ScrollView: where to put the actual scroll can be defined with an insideInset as mentioned in this question.

<ScrollView
android:id="@+id/view2"
android:layout_width="100dip"
android:layout_height="120dip"
android:padding="8dip"
android:scrollbarStyle="insideInset"
android:background="@android:color/white"
android:overScrollMode="never">

How many WindowInsets are there?

WindowInsets describes a set of insets for window content.
In other words, WindowInsets has one rect of the available area for the app (and has other info like isRound). Available area excludes the rect of StatusBar and NavigationBar.

If you just want to know the height of StatusBar and NavigationBar, check this.

You can get WindowInsets like following.
Following example uses WindowInsetsCompat for compatibility.

In your style.xml:

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

In your AndroidManifest.xml

<application
...
android:theme="@style/AppTheme">

...

</application>

In your layout xml: (fitsSystemWindows should be set to get WindowInsets.)

<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/container"
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"/>

</FrameLayout>

In your Activity (or any place):

public class MainActivity extends AppCompatActivity {

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

View container = findViewById(R.id.container);

ViewCompat.setOnApplyWindowInsetsListener(container, new OnApplyWindowInsetsListener() {
@Override
public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {

//you can do something with insets.
int statusBar = insets.getSystemWindowInsetTop(); //this is height of statusbar
int navigationBar = insets.getStableInsetBottom(); //this is height of navigationbar
Log.d("MainActivity", String.format("%s %s", statusBar, navigationBar));

ViewCompat.onApplyWindowInsets(v, insets);
return insets;
}
});
}
}

WindowInsets is like this:

Sample Image

Show keyboard over Scaffold's bottomBar in Jetpack Compose and apply proper inset paddings

According to Accompanist Insets migration, LocalWindowInsets.current.ime should be replaced with WindowInsets.ime.

It doesn't have isVisible for now, until this bug is fixed. Here's how I've re-created it for now:

val WindowInsets.Companion.isImeVisible: Boolean
@Composable
get() {
val density = LocalDensity.current
val ime = this.ime
return remember {
derivedStateOf {
ime.getBottom(density) > 0
}
}.value
}

Usage:

val isImeVisible = WindowInsets.isImeVisible

This should work with your old remember(isImeVisible) code.

Go edge-to-edge on Android correctly with WindowInsets

Edit 2022-01-16:

Nowadays we can use https://google.github.io/accompanist/insets to get insets for jetpack compose and just add WindowCompat.setDecorFitsSystemWindows(window, false) like @shogun-nassar points out correctly in his answer.


Original answer:

To provide a final answer:

Do not use android:fitsSystemWindows anywhere but apply insets manually to any view at the edge of the screen which would otherwise slip behind the system bars (e.g. AppBarLayout or FloatingActionButton).

I wrote some helpers to add the insets to either padding or margin, respecting any previously added one (needs androidx.core:core:1.2.0-alpha01):

fun View.addSystemWindowInsetToPadding(
left: Boolean = false,
top: Boolean = false,
right: Boolean = false,
bottom: Boolean = false
) {
val (initialLeft, initialTop, initialRight, initialBottom) =
listOf(paddingLeft, paddingTop, paddingRight, paddingBottom)

ViewCompat.setOnApplyWindowInsetsListener(this) { view, insets ->
view.updatePadding(
left = initialLeft + (if (left) insets.systemWindowInsetLeft else 0),
top = initialTop + (if (top) insets.systemWindowInsetTop else 0),
right = initialRight + (if (right) insets.systemWindowInsetRight else 0),
bottom = initialBottom + (if (bottom) insets.systemWindowInsetBottom else 0)
)

insets
}
}

fun View.addSystemWindowInsetToMargin(
left: Boolean = false,
top: Boolean = false,
right: Boolean = false,
bottom: Boolean = false
) {
val (initialLeft, initialTop, initialRight, initialBottom) =
listOf(marginLeft, marginTop, marginRight, marginBottom)

ViewCompat.setOnApplyWindowInsetsListener(this) { view, insets ->
view.updateLayoutParams {
(this as? ViewGroup.MarginLayoutParams)?.let {
updateMargins(
left = initialLeft + (if (left) insets.systemWindowInsetLeft else 0),
top = initialTop + (if (top) insets.systemWindowInsetTop else 0),
right = initialRight + (if (right) insets.systemWindowInsetRight else 0),
bottom = initialBottom + (if (bottom) insets.systemWindowInsetBottom else 0)
)
}
}

insets
}
}

As an example, simply call fab.addSystemWindowInsetToMargin(bottom = true) and the FAB will be moved up above the navigation bar. Or app_bar.addSystemWindowInsetToPadding(top = true) to keep the app bar below the status bar (mind the margin/padding difference).

What are insets in android?

Suppose you have a TextView and you need to add a background to the TextView. But, on the other hand, you don't want the background to scan the entire View (TextView), therefore, you need to use Insets.

A Drawable that insets another Drawable by a specified distance or
fraction of the content bounds. This is used when a View needs a
background that is smaller than the View's actual bounds.

The background can be a Drawable. Hence you need to use the <inset> attribute inside your xml file (activity_main.xml) "For Example".

Then, after using the <inset> tag, you can specify some attributes such as:

<inset
android:drawable="@drawable/(Enter the file name under drawable)"
android:insetBottom="4dp"
android:insetTop="4dp"/>

For more information please have a look at InsetDrawable on Android developer.com

Hope this helps!

onApplyWindowInsets(WindowInsets insets) not getting called

Once the insets are consumed, propagation down the hierarchy stops. It looks like something higher up is consuming what's available. See isConsumed() of WindowsInset.

Check if these insets have been fully consumed.

Insets are considered "consumed" if the applicable consume* methods have been called such that all insets have been set to zero. This affects propagation of insets through the view hierarchy; insets that have not been fully consumed will continue to propagate down to child views.



Related Topics



Leave a reply



Submit