La plataforma de Android es responsable de dibujar la IU del sistema, como la barra de estado y la de navegación. Esta IU del sistema se muestra independientemente de la app que use el usuario.
WindowInsets
proporciona información sobre la IU del sistema para garantizar que tu app se dibuje en el área correcta y que la IU del sistema no la oculte.
En Android 14 (nivel de API 34) y versiones anteriores, la IU de tu app no se dibuja debajo de las barras del sistema ni de los cortes de pantalla de forma predeterminada.
En Android 15 (nivel de API 35) y versiones posteriores, tu app se dibuja debajo de las barras del sistema y muestra recortes una vez que se orienta al SDK 35. Esto genera una experiencia del usuario más fluida y permite que tu app aproveche al máximo el espacio de la ventana disponible.
Mostrar contenido detrás de la IU del sistema se denomina borde a borde. En esta página, aprenderás sobre los diferentes tipos de inserciones, cómo pasar de borde a borde y cómo usar las APIs de inserciones para animar tu IU y asegurarte de que el contenido de tu app no esté oculto por elementos de la IU del sistema.
Conceptos básicos de los elementos insertados
Cuando una app se muestra de borde a borde, debes asegurarte de que la IU del sistema no oculte el contenido y las interacciones importantes. Por ejemplo, si un botón se coloca detrás de la barra de navegación, es posible que el usuario no pueda hacer clic en él.
El tamaño de la IU del sistema y la información sobre dónde se coloca se especifican mediante insertos.
Cada parte de la IU del sistema tiene un tipo de inserto correspondiente que describe su tamaño y dónde se coloca. Por ejemplo, las inserciones de la barra de estado proporcionan el tamaño y la posición de la barra de estado, mientras que las inserciones de la barra de navegación proporcionan el tamaño y la posición de la barra de navegación. Cada tipo de inserto consta de cuatro dimensiones de píxeles: superior, izquierda, derecha e inferior. Estas dimensiones especifican hasta dónde se extiende la IU del sistema desde los lados correspondientes de la ventana de la app. Por lo tanto, para evitar superponerse con ese tipo de IU del sistema, la IU de la app debe insertarse en esa cantidad.
Estos tipos de inserciones integradas de Android están disponibles a través de WindowInsets
:
Las inserciones que describen las barras de estado Estas son las barras superiores de la IU del sistema que contienen íconos de notificaciones y otros indicadores. |
|
Los recuadros de la barra de estado para cuando están visibles Si las barras de estado están ocultas actualmente (debido a que se ingresó al modo de pantalla completa envolvente), los inserciones de la barra de estado principal estarán vacíos, pero no lo estarán. |
|
Las inserciones que describen las barras de navegación Estas son las barras de la IU del sistema que se encuentran a la izquierda, a la derecha o en la parte inferior del dispositivo y que describen la barra de tareas o los íconos de navegación. Estos pueden cambiar durante el tiempo de ejecución según el método de navegación preferido del usuario y la interacción con la barra de tareas. |
|
La barra de navegación se inserta cuando está visible. Si las barras de navegación están ocultas (debido a que se ingresó al modo de pantalla completa envolvente), los insertos de la barra de navegación principal estarán vacíos, pero estos insertos no estarán vacíos. |
|
El inserto que describe la decoración de la ventana de la IU del sistema si está en una ventana de formato libre, como la barra de título superior. |
|
La barra de subtítulos se inserta cuando están visibles. Si las barras de subtítulos están ocultas, los recuadros de la barra de subtítulos principal estarán vacíos, pero no lo estarán. |
|
La unión de las inserciones de la barra del sistema, que incluyen las barras de estado, las barras de navegación y la barra de leyendas |
|
Los rellenos de la barra del sistema para cuando están visibles Si las barras del sistema están ocultas (debido a que se ingresó al modo de pantalla completa inmersivo), los inserciones de la barra del sistema principal estarán vacías, pero no serán vacías. |
|
Las inserciones que describen la cantidad de espacio en la parte inferior que ocupa el teclado en pantalla |
|
Las inserciones que describen la cantidad de espacio que ocupaba el teclado en software antes de la animación actual del teclado. |
|
Las inserciones que describen la cantidad de espacio que ocupará el teclado en pantalla después de la animación actual del teclado. |
|
Es un tipo de inserciones que describen información más detallada sobre la IU de navegación y que proporciona la cantidad de espacio en la que el sistema controlará los "toques", y no la app. En el caso de las barras de navegación transparentes con navegación por gestos, algunos elementos de la app se pueden presionar a través de la IU de navegación del sistema. |
|
Los elementos que se pueden presionar se insertan cuando están visibles. Si los elementos que se pueden presionar están ocultos (debido a que se ingresó al modo de pantalla completa envolvente), los elementos insertados principales que se pueden presionar estarán vacíos, pero estos insertos no estarán vacíos. |
|
Son los recuadros que representan la cantidad de recuadros en los que el sistema interceptará los gestos para la navegación. Las apps pueden especificar manualmente la forma de controlar una cantidad limitada de estos gestos a través de |
|
Es un subconjunto de los gestos del sistema que el sistema siempre controlará y que no se puede inhabilitar a través de |
|
Las inserciones que representan la cantidad de espacio necesaria para evitar superponerse con un corte de pantalla (muesca o orificio) |
|
Las inserciones que representan las áreas curvas de una pantalla en cascada. Una pantalla de cascada tiene áreas curvas a lo largo de los bordes de la pantalla donde esta comienza a doblarse a lo largo de los lados del dispositivo. |
Estos tipos se resumen en tres tipos de inserciones "seguras" que garantizan que el contenido no se oculte:
Estos tipos de inserciones "seguras" protegen el contenido de diferentes maneras, según las inserciones subyacentes de la plataforma:
- Usa
WindowInsets.safeDrawing
para proteger el contenido que no se debe dibujar debajo de ninguna IU del sistema. Este es el uso más común de los insertos: evitar dibujar contenido que la IU del sistema oculta (ya sea de forma parcial o completa). - Usa
WindowInsets.safeGestures
para proteger el contenido con gestos. De esta manera, se evita que los gestos del sistema entren en conflicto con los gestos de la app (como los de las hojas inferiores, los carruseles o los juegos). - Usa
WindowInsets.safeContent
como una combinación deWindowInsets.safeDrawing
yWindowInsets.safeGestures
para garantizar que el contenido no tenga superposición visual ni superposición de gestos.
Configuración de las inserciones
Para permitir que tu app tenga control total sobre dónde dibuja el contenido, sigue estos pasos de configuración. Sin estos pasos, es posible que tu app dibuje colores negros o sólidos detrás de la IU del sistema, o que no se anime de forma síncrona con el teclado en software.
- Orienta el SDK a la versión 35 o una posterior para aplicar el diseño de pantalla completa en Android 15 y versiones posteriores. Tu app se muestra detrás de la IU del sistema. Puedes ajustar la IU de tu app controlando los inserciones.
- De manera opcional, llama a
enableEdgeToEdge()
enActivity.onCreate()
, lo que permite que tu app sea de borde a borde en versiones anteriores de Android. Establece
android:windowSoftInputMode="adjustResize"
en la entradaAndroidManifest.xml
de tu actividad. Este parámetro de configuración permite que tu app reciba el tamaño del IME de software como inserciones, que puedes usar para rellenar y diseñar el contenido de forma adecuada cuando el IME aparece y desaparece en tu app.<!-- in your AndroidManifest.xml file: --> <activity android:name=".ui.MainActivity" android:label="@string/app_name" android:windowSoftInputMode="adjustResize" android:theme="@style/Theme.MyApplication" android:exported="true">
APIs de Compose
Una vez que tu actividad haya tomado el control de la administración de todos los inserciones, puedes usar las APIs de Compose para asegurarte de que el contenido no se oculte y que los elementos interactivos no se superpongan con la IU del sistema. Estas APIs también sincronizan el diseño de tu app con los cambios de insert.
Por ejemplo, este es el método más básico para aplicar los insertos al contenido de toda tu app:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() setContent { Box(Modifier.safeDrawingPadding()) { // the rest of the app } } }
Este fragmento aplica las inserciones de ventana safeDrawing
como relleno alrededor del contenido completo de la app. Si bien esto garantiza que los elementos interactivos no se superpongan con la IU del sistema, también significa que nada de la app se dibujará detrás de la IU del sistema para lograr un efecto de borde a borde. Para aprovechar al máximo la ventana, debes ajustar dónde se aplican las inserciones en cada pantalla o componente.
Todos estos tipos de inserciones se animan automáticamente con animaciones del IME que se portaron a la API 21. Por extensión, todos tus diseños que usan estos rellenos también se animan automáticamente a medida que cambian los valores de relleno.
Existen dos formas principales de usar estos tipos de inserciones para ajustar tus diseños componibles: modificadores de padding y modificadores de tamaño de inserción.
Modificadores de padding
Modifier.windowInsetsPadding(windowInsets: WindowInsets)
aplica las inserciones de ventana determinadas como padding, actuando de la misma manera que lo haría Modifier.padding
.
Por ejemplo, Modifier.windowInsetsPadding(WindowInsets.safeDrawing)
aplica los inserciones de dibujo seguras como relleno en los 4 lados.
También hay varios métodos de utilidad integrados para los tipos de inserciones más comunes.
Modifier.safeDrawingPadding()
es uno de esos métodos, equivalente a Modifier.windowInsetsPadding(WindowInsets.safeDrawing)
. Existen modificadores similares para los otros tipos de inserciones.
Modificadores de tamaño de la inserción
Los siguientes modificadores aplican una cantidad de inserciones de ventana configurando el tamaño del componente para que sea el tamaño de las inserciones:
Aplica el lado inicial de windowInsets como el ancho (como |
|
Aplica el lado final de windowInsets como el ancho (como |
|
Aplica el lado superior de windowInsets como la altura (como |
|
|
Aplica el lado inferior de windowInsets como la altura (como |
Estos modificadores son particularmente útiles para ajustar el tamaño de un Spacer
que ocupa el espacio de los elementos intercalados:
LazyColumn( Modifier.imePadding() ) { // Other content item { Spacer( Modifier.windowInsetsBottomHeight( WindowInsets.systemBars ) ) } }
Consumo de intercalación
Los modificadores de padding de inset (windowInsetsPadding
y ayudantes como safeDrawingPadding
) consumen automáticamente la parte de los insets que se aplican como padding. Mientras se profundiza en el árbol de composición, los modificadores de padding de inset anidados y los modificadores de tamaño de inset saben que algunos de los insets ya se consumieron con los modificadores de padding de inset externos y evitan usar la misma parte de los insets más de una vez, lo que generaría demasiado espacio adicional.
Los modificadores de tamaño de inserciones también evitan usar la misma parte de inserciones más de una vez si ya se consumieron. Sin embargo, como cambian su tamaño directamente, no consumen inserciones.
Como resultado, los modificadores de padding anidados cambian automáticamente la cantidad de padding aplicado a cada elemento componible.
Si observas el mismo ejemplo de LazyColumn
que antes, el modificador imePadding
cambia el tamaño de LazyColumn
. Dentro de LazyColumn
, el último elemento tiene el tamaño de la altura de la parte inferior de las barras del sistema:
LazyColumn( Modifier.imePadding() ) { // Other content item { Spacer( Modifier.windowInsetsBottomHeight( WindowInsets.systemBars ) ) } }
Cuando el IME está cerrado, el modificador imePadding()
no aplica padding, ya que el IME no tiene altura. Como el modificador imePadding()
no aplica relleno, no se consumen inserciones, y la altura de Spacer
será del tamaño del lado inferior de las barras del sistema.
Cuando se abre el IME, los inserciones del IME se animan para que coincidan con el tamaño del IME, y el modificador imePadding()
comienza a aplicar padding inferior para cambiar el tamaño del LazyColumn
a medida que se abre el IME. A medida que el modificador imePadding()
comienza a aplicar el padding inferior, también comienza a consumir esa cantidad de inserciones. Por lo tanto, la altura de Spacer
comienza a disminuir, ya que el modificador imePadding()
ya aplicó parte del espaciado para las barras del sistema. Una vez que el modificador imePadding()
aplica una cantidad de padding inferior que es mayor que las barras del sistema, la altura de Spacer
es cero.
Cuando se cierra el IME, los cambios se producen a la inversa: Spacer
comienza a expandirse desde una altura de cero una vez que imePadding()
aplica menos que el lado inferior de las barras del sistema, hasta que finalmente Spacer
coincide con la altura del lado inferior de las barras del sistema una vez que el IME se anima por completo.
Este comportamiento se logra a través de la comunicación entre todos los modificadores windowInsetsPadding
y puede verse influenciado de otras maneras.
Modifier.consumeWindowInsets(insets: WindowInsets)
también consume inserciones de la misma manera que Modifier.windowInsetsPadding
, pero no aplica las inserciones consumidas como padding. Esto es útil en combinación con los modificadores de tamaño de los rellenos para indicar a los elementos hermanos que ya se consumió una cantidad determinada de rellenos:
Column(Modifier.verticalScroll(rememberScrollState())) { Spacer(Modifier.windowInsetsTopHeight(WindowInsets.systemBars)) Column( Modifier.consumeWindowInsets( WindowInsets.systemBars.only(WindowInsetsSides.Vertical) ) ) { // content Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime)) } Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.systemBars)) }
Modifier.consumeWindowInsets(paddingValues: PaddingValues)
se comporta de manera muy similar a la versión con un argumento WindowInsets
, pero toma un PaddingValues
arbitrario para consumir. Esto es útil para informar a los elementos secundarios cuando el padding o el espaciado se proporcionan a través de algún otro mecanismo que no sean los modificadores de padding intercalado, como un Modifier.padding
ordinario o espaciadores de altura fija:
Column(Modifier.padding(16.dp).consumeWindowInsets(PaddingValues(16.dp))) { // content Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime)) }
En los casos en que se necesiten las inserciones de ventana sin procesar sin consumo, usa los valores de WindowInsets
directamente o usa WindowInsets.asPaddingValues()
para mostrar un PaddingValues
de las inserciones que no se ven afectadas por el consumo.
Sin embargo, debido a las advertencias que se indican a continuación, prefiere usar los modificadores de padding de inserciones de ventana y los modificadores de tamaño de inserciones de ventana siempre que sea posible.
Insets y fases de Jetpack Compose
Compose usa las APIs principales subyacentes de AndroidX para actualizar y animar los insertos, que usan las APIs de la plataforma subyacentes que administran los insertos. Debido a ese comportamiento de la plataforma, los insertos tienen una relación especial con las fases de Jetpack Compose.
El valor de los rellenos se actualiza después de la fase de composición, pero antes de la fase de diseño. Esto significa que, por lo general, leer el valor de las inserciones en la composición usa un valor de las inserciones que está un fotograma atrasado. Los modificadores integrados que se describen en esta página se compilan para retrasar el uso de los valores de los rellenos hasta la fase de diseño, lo que garantiza que los valores de relleno se usen en el mismo marco a medida que se actualizan.
Animaciones del IME del teclado con WindowInsets
Puedes aplicar Modifier.imeNestedScroll()
a un contenedor de desplazamiento para abrir y cerrar el IME automáticamente cuando te desplazas hasta la parte inferior del contenedor.
class WindowInsetsExampleActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) WindowCompat.setDecorFitsSystemWindows(window, false) setContent { MaterialTheme { MyScreen() } } } } @OptIn(ExperimentalLayoutApi::class) @Composable fun MyScreen() { Box { LazyColumn( modifier = Modifier .fillMaxSize() // fill the entire window .imePadding() // padding for the bottom for the IME .imeNestedScroll(), // scroll IME at the bottom content = { } ) FloatingActionButton( modifier = Modifier .align(Alignment.BottomEnd) .padding(16.dp) // normal 16dp of padding for FABs .navigationBarsPadding() // padding for navigation bar .imePadding(), // padding for when IME appears onClick = { } ) { Icon(imageVector = Icons.Filled.Add, contentDescription = "Add") } } }
Compatibilidad con inserciones para componentes de Material 3
Para facilitar el uso, muchos de los elementos componibles integrados de Material 3 (androidx.compose.material3
) controlan las inserciones por su cuenta, según la forma en que se colocan los elementos componibles en tu app según las especificaciones de Material.
Elementos componibles que manejan inserciones
A continuación, se muestra una lista de los componentes de Material que controlan automáticamente las inserciones.
Barras de la app
TopAppBar
/SmallTopAppBar
/CenterAlignedTopAppBar
/MediumTopAppBar
/LargeTopAppBar
: Aplica los lados superior y horizontal de las barras del sistema como relleno, ya que se usa en la parte superior de la ventana.BottomAppBar
: Aplica los lados inferior y horizontal de las barras del sistema como padding.
Contenedores de contenido
ModalDrawerSheet
/DismissibleDrawerSheet
/PermanentDrawerSheet
(contenido dentro de un panel lateral de navegación modal): Aplica inserciones verticales y de inicio al contenido.ModalBottomSheet
: Aplica los rellenos inferiores.NavigationBar
: Aplica los rellenos inferior y horizontal.NavigationRail
: Aplica los rellenos vertical y inicio.
Scaffold
De forma predeterminada, Scaffold
proporciona inserciones como el parámetro paddingValues
para que las consumas y uses.
Scaffold
no aplica los insertos al contenido; esta responsabilidad es tuya.
Por ejemplo, para consumir estos inserciones con un LazyColumn
dentro de un Scaffold
, haz lo siguiente:
Scaffold { innerPadding -> // innerPadding contains inset information for you to use and apply LazyColumn( // consume insets as scaffold doesn't do it by default modifier = Modifier.consumeWindowInsets(innerPadding), contentPadding = innerPadding ) { items(count = 100) { Box( Modifier .fillMaxWidth() .height(50.dp) .background(colors[it % colors.size]) ) } } }
Cómo anular los rellenos predeterminados
Puedes cambiar el parámetro windowInsets
que se pasa al elemento componible para configurar su comportamiento. Este parámetro puede ser un tipo diferente de inserto de ventana para aplicar en su lugar, o bien se puede inhabilitar pasando una instancia vacía: WindowInsets(0, 0, 0, 0)
.
Por ejemplo, para inhabilitar el control de inserciones en LargeTopAppBar
, establece el parámetro windowInsets
en una instancia vacía:
LargeTopAppBar( windowInsets = WindowInsets(0, 0, 0, 0), title = { Text("Hi") } )
Interoperabilidad con los elementos insertados del sistema de View
Es posible que debas anular los rellenos predeterminados cuando tu pantalla tenga elementos View y código de Compose en la misma jerarquía. En este caso, debes ser explícito en cuál debe consumir los insertos y cuál debe ignorarlos.
Por ejemplo, si tu diseño más externo es un diseño de View de Android, debes consumir los inserciones en el sistema de View y, luego, ignorarlas para Compose.
Como alternativa, si tu diseño más externo es un elemento componible, debes consumir los inserciones en Compose y rellenar los elementos componibles AndroidView
según corresponda.
De forma predeterminada, cada ComposeView
consume todos los inserciones en el nivel de consumo WindowInsetsCompat
. Para cambiar este comportamiento predeterminado, establece ComposeView.consumeWindowInsets
en false
.
Protección de la barra del sistema
Una vez que tu app se oriente al SDK 35 o versiones posteriores, se aplicará el diseño de pantalla completa. La barra de estado del sistema y las barras de navegación por gestos son transparentes, pero la barra de navegación con tres botones es traslúcida.
Para quitar la protección en segundo plano predeterminada de la navegación con tres botones traslúcida, establece Window.setNavigationBarContrastEnforced
en false
.
Recursos
- Barras del sistema de Android, guía de diseño de barras del sistema
- Now in Android: Una app para Android totalmente funcional que se compiló por completo con Kotlin y Jetpack Compose.
- Cómo cumplir con los requisitos de borde a borde de Android 15: Un codelab en el que se explica la aplicación de los requisitos de borde a borde de Android 15
- Sugerencias para controlar los márgenes de Android 15
- Obtén una vista previa y prueba la IU de borde a borde de tu app
- 3 aspectos para mejorar la experiencia de tu app para Android: Borde a borde, Atrás predictivo y Vista rápida: un video de YouTube sobre la aplicación forzosa de borde a borde en Android 15
- Edge-to-edge and insets | Compose Tips: Un video de YouTube en el que se muestra cómo controlar los elementos insertados para dibujar de borde a borde.
Recomendaciones para ti
- Nota: El texto del vínculo se muestra cuando JavaScript está desactivado
- Componentes y diseños de Material
- Cómo migrar
CoordinatorLayout
a Compose - Otras consideraciones