Android Compose的Window Insets

In addition to the app’s content area, there are a few other fixed elements that appear on the phone’s screen. The status bar at the top, bangs, the navigation bar at the bottom, and the input keyboard are the UI of the system, also known as Insets.

As shown in the figure:

The status bar at the top is usually used to display notifications, device status, etc. The bottom navigation bar usually shows three navigation buttons: Back, Home, and Recent. Together, they are called System bars.

The Android Insets class describes the offset size information, and indeed we are more concerned with the size information of the system UI in development.

In this paper, made after the UI Compose, with the aid of Accompanist Insets: Google. Making. IO/Accompanist… . How to do several common Insets related situations.

The content area

Going Edge-to-Edge

Compose Compose Compose Compose Compose Compose Compose Compose Compose Compose Compose Compose Compose Compose Compose Compose Compose Compose Compose Compose Compose

How about edge-to-edge app content on these System bars? Of course you can.

Two concepts are clarified here:

  • Edge-to-edge: The content of the app is drawn behind System bars, which still exist in translucent form.
  • Unlike the Immersive mode, which hides System bars, the app is fully full-screen, mostly for watching videos, painting, etc.

The content area extends to System bars

Extending the content to the Status bar and Navigation bar areas is easy with just one line of code:

WindowCompat.setDecorFitsSystemWindows(window, false)
Copy the code

This value defaults to true and represents the default behavior: the content of the app will automatically find the inline area to draw. When set to false, the content of the app extends to the lower level of System bars.

The difference is shown in the following figure: The left side is displayed by default, and the right side is displayed after adding the setting of flag false:

Well, the content is drawn out, but it’s obscured.

Add a few lines to change the color you like:

val systemUiController = rememberSystemUiController()
val useDarkIcons = MaterialTheme.colors.isLight

SideEffect {
    systemUiController.setSystemBarsColor(
        color = Color.Green.copy(alpha = 0.1 f),
        darkIcons = useDarkIcons
    )
}
Copy the code

Here we have changed system bars, i.e. status bar and Navigation bar. There are ways to change just one. For the demo, set the color to transparent green (left image); In normal use, you might want to use Color.Transparent.

Extended but embedded

After making several pages of UI, I found that some content was covered in the status bar and the bottom, and the experience was not very good. Can you get the text out of there?

So we add this dependency: Insets for Jetpack Compose

Two simple lines leave out the distance between the top and the bottom:

ProvideWindowInsets {
    Sample1(modifier = Modifier.systemBarsPadding())
}
Copy the code

Wait, if you ignore the system bars color setting. It looks exactly the same as the default at the beginning.

So if we can delete directly WindowCompat. SetDecorFitsSystemWindows (window, false) this line, it is good to use the default Settings?

  • Yes, if that’s what you need.
  • No. If you need to draw the background of your app; If you still have input field processing.

If the requirement is to extend the background, the text is embedded. We add different padding to the upper and lower elements:

Column(
        modifier = modifier.fillMaxSize()
                .background(color = Color.Blue.copy(alpha = 0.3 f)),
        verticalArrangement = Arrangement.SpaceBetween
) {
    Text(
            modifier = Modifier.fillMaxWidth()
                    .background(color = Color.Yellow.copy(alpha = 0.5 f))
                    .statusBarsPadding(),
            text = "Top Text",
            style = MaterialTheme.typography.h2
    )
    Text(text = "Content". style = MaterialTheme.typography.h2) Text( modifier = Modifier.fillMaxWidth() .navigationBarsPadding() .background(color  = Color.Yellow.copy(alpha =0.5 f)),
            text = "Bottom Text",
            style = MaterialTheme.typography.h2
    )
}
Copy the code

After running, it is shown on the right in the figure below:

Notice the sequence of the modifier here, the color extending from the top to the bottom is different, and the color extending from the bottom is actually Column.

On the left is the insets padding added to the layout, which is the same as the default UI if you use System bars.

Specific requirements can be customized.

Padding for LazyColumn and content padding

I have a very long LazyColumn, how do I display it in edge-to-edge design? Here are three options:

  1. List full screen:LazyColumn {}
  2. List with upper and lower padding:LazyColumn(modifier = Modifier.systemBarsPadding()) {}
  3. List with Content padding:
LazyColumn(
    contentPadding = rememberInsetsPaddingValues(
        insets = LocalWindowInsets.current.systemBars,
        applyTop = true,
        applyBottom = true, {}))Copy the code

The content padding is just the padding on top of the first item and under the last item, so that the content can be full-screen in the middle of scrolling, and the padding is only displayed when it reaches the end or bottom.

Content padding is better illustrated with a GIF:

The content area handles the summary

Insets this library provides several modifiers:

  • Modifier.statusBarsPadding()
  • Modifier.navigationBarsPadding()
  • Modifier.systemBarsPadding()
  • Modifier.imePadding()
  • Modifier.navigationBarsWithImePadding()
  • Modifier.cutoutPadding()StatusBarPadding is top, navigationBarsPadding is Bottom. You don’t even have to think about it.

If these do not satisfy your needs, you can also use the size directly:

  • Modifier.statusBarsHeight()
  • Modifier.navigationBarsHeight()
  • Modifier.navigationBarsWidth()

Or more directly with LocalWindowInsets. Current oneself want an inset type size.

Input box elements and keyboard

The on-screen keyboard, also called IME (Input Method Editor), will pop up when you click the Input box. IME is also an Inset.

The input box is blocked by the keyboard

When the input box is in the upper half of the screen, there is no need to consider the problem of keyboard occlusion. But when the input field is in the bottom half of the screen, we need to make the input field fully visible when the keyboard pops out.

A few things are needed to solve this problem:

  • The Activity ofandroid:windowSoftInputMode="adjustResize", which means that when the keyboard pops up, the Activity changes the size of the layout.
  • Modifier.imePaddingAdd a bottom padding exactly equal to the height of the keyboard. It is usually the parent layout of the input field, which layer is added depending on the situation.
  • If both of the above Settings still do not fully display the input box, you may need to add a bit of strong wake up behavior.

According to the comment under the issue, you can use the Modifier to bring yourself into View when the UI gets the focus.

@ExperimentalComposeUiApi
fun Modifier.bringIntoViewAfterImeAnimation(a): Modifier = composed {
    val imeInsets = LocalWindowInsets.current.ime
    var focusState byremember { mutableStateOf<FocusState? > (null)}val relocationRequester = remember { RelocationRequester() }

    LaunchedEffect(
        imeInsets.isVisible,
        imeInsets.animationInProgress,
        focusState,
        relocationRequester
    ) {
        if(imeInsets.isVisible && ! imeInsets.animationInProgress && focusState? .isFocused ==true) {
            relocationRequester.bringIntoView()
        }
    }

    relocationRequester(relocationRequester)
        .onFocusChanged { focusState = it }
}
Copy the code

The ReloactionRequest has been deprecated, and the new version of Compose is called BringIntoViewRequester.

IME padding calculation and layout

The.imepadding () value changes, from 0 if no keyboard is present to keyboard height when a keyboard is present.

To calculate the height of the keyboard pop-up note:

  • In the simplest case, just use it.imePadding()When done, the bottom padding of the layout automatically fits into the IME.
  • If the whole navigation bar already has the height, it can be considered.navigationBarsWithImePadding(), which takes the maximum height of the IME and navigation bar.
  • If a white bar appears at the top of the keyboard, it means that there is too much padding, and either inner padding is already in the layout or it has been addednavigationBarsPaddingAt this point you can do a subtraction.

Like this one:

LazyColumn(
    contentPadding = PaddingValues(
        bottom = with(LocalDensity.current) {
            LocalWindowInsets.current.ime.bottom.toDp() - innerPadding.bottom
        }.coerceAtLeast(0.dp)
    )
) { / *... * / }
Copy the code

Where the imePadding is placed depends on what area is displayed. The covered area is displayed above the keyboard.

For example, let’s have an interface with an input field.

We give it a whole set a. NavigationBarsWithImePadding (), when no keyboard said, at the bottom of the left navigation bar height, make the height of the keyboard with the keyboard:

Column(
    modifier = Modifier.fillMaxSize().statusBarsPadding().navigationBarsWithImePadding()
        .background(color = Color.Cyan.copy(alpha = 0.2 f)),
    verticalArrangement = Arrangement.SpaceBetween
) {
    Text(
        modifier = Modifier.fillMaxWidth()
            .background(color = Color.Yellow.copy(alpha = 0.5 f)),
        text = "Top Text",
        style = MaterialTheme.typography.h2
    )
    Text(text = "Content", style = MaterialTheme.typography.h2)
    MyTextField("Text Field 1")
    MyTextField("Text Field 2")
    Text(
        modifier = Modifier.fillMaxWidth()
            .background(color = Color.Yellow.copy(alpha = 0.5 f)),
        text = "Bottom Text",
        style = MaterialTheme.typography.h2
    )
}
Copy the code

When the keyboard pops up, the Bottom Text is also topped up because the imePadding applies to the entire layout.

If we change this to cover only the part of the input field, then the keyboard will not top the UI at the bottom:

    Column(
        modifier = Modifier.fillMaxSize().statusBarsPadding()
            .background(color = Color.Cyan.copy(alpha = 0.2 f)),
        verticalArrangement = Arrangement.SpaceBetween
    ) {
        Text(
            modifier = Modifier.fillMaxWidth()
                .background(color = Color.Yellow.copy(alpha = 0.5 f)),
            text = "Top Text",
            style = MaterialTheme.typography.h2
        )
        Text(text = "Content". style = MaterialTheme.typography.h2) Text( modifier = Modifier.fillMaxWidth() .background(color = Color.Yellow.copy(alpha =0.5 f)),
            text = "Bottom Text",
            style = MaterialTheme.typography.h2
        )
    }
Copy the code

See figure for two effects:

The keyboard section summarizes the extension

Summary: Input box keyboard processing includes:

  • adjustResize.
  • Set the bottom padding properly: Where to set it and how much to set it.
  • Let the View actively bring itself to the visible position.

The Insets library also provides examples of keyboards disappearing and appearing as they scroll. You can take a look if you are interested.

CSST Insets use summary

Csist Insets helps us make two parts:

  • Get various insets information and useCompositionLocalProviderTo provide.
  • Inside the Provider, you can use the Modifier to obtain the Modifier or dimensions that are directly available.

However, there are some caveats to using this library, such as:

  • If you forget to setWindowCompat.setDecorFitsSystemWindows(window, false)And you get 0.
  • ProvideWindowInsets:consumeWindowInsetsThe default value istrueFalse is recommended so that the inner UI can continue to use these inset values.
@Composable
fun ProvideWindowInsets(
    consumeWindowInsets: Boolean = true,
    windowInsetsAnimationsEnabled: Boolean = true,
    content: @Composable() - >Unit
)
Copy the code
  • If used nested in a layoutProvideWindowInsets, may not work as expected.

References

  • Lay out your app within window insets
  • Accompanist Insets: Google. Making. IO/Accompanist…
  • Sample: github.com/google/acco…
  • Video: Animating your keyboard using WindowInsets