La compatibilidad con diferentes tamaños de pantalla permite que más usuarios y una mayor variedad de dispositivos puedan acceder a tu app.
Para admitir tantos tamaños de pantalla como sea posible, ya sea en diferentes pantallas de dispositivos o en diferentes ventanas de apps en el modo multiventana, diseña los diseños de tu app para que sean responsivos y adaptables. Los diseños responsivos o adaptables proporcionan una experiencia del usuario optimizada sin importar el tamaño de la pantalla, lo que permite que tu app funcione en teléfonos, tablets, dispositivos plegables y dispositivos ChromeOS, en orientaciones verticales y horizontales, y en configuraciones de pantalla que pueden cambiar de tamaño, como el modo de pantalla dividida y el modo de ventanas de escritorio.
Los diseños responsivos o adaptables cambian según el espacio de visualización disponible. Los cambios van desde pequeños ajustes de diseño que llenan el espacio (diseño responsivo) hasta el reemplazo completo de un diseño por otro para que tu app pueda adaptarse mejor a diferentes tamaños de pantalla (diseño adaptable).
Como kit de herramientas declarativas de IU, Jetpack Compose es ideal para diseñar e implementar diseños que cambian de forma dinámica para renderizar contenido de manera diferente en distintos tamaños de pantalla.
Habilidades de Android
Ver en GitHubCómo hacer que tu app sea adaptable
adaptive para actualizar la IU de tu app de modo que se adapte a diferentes dispositivos y pantallas Android.android skills add adaptiveRealiza cambios en diseños grandes para elementos componibles explícitos a nivel del contenido
Los elementos componibles a nivel de la app y del contenido ocupan todo el espacio de visualización disponible para tu app. En el caso de estos tipos de elementos componibles, puede ser conveniente cambiar el diseño general de tu app en pantallas grandes.
Evita usar valores físicos de hardware para tomar decisiones de diseño. Puede ser tentador tomar decisiones basadas en un valor fijo y tangible (¿el dispositivo es una tablet?, ¿la pantalla física tiene una relación de aspecto determinada?), pero las respuestas a estas preguntas pueden no ser útiles para determinar el espacio disponible para tu IU.
En las tablets, es posible que una app se ejecute en el modo multiventana, lo que significa que esta podría dividir la pantalla con otra app. En el modo de ventanas de escritorio o en ChromeOS, una app podría ejecutarse en una ventana que puede cambiar de tamaño. Incluso puede haber más de una pantalla física, como sucede con un dispositivo plegable. En todos estos casos, el tamaño físico de la pantalla no es relevante para decidir la forma en la que se muestra el contenido.
En cambio, toma decisiones según la parte real de la pantalla que se asigna a tu app, como se describe en las métricas de ventana actuales que proporciona la biblioteca WindowManager de Jetpack. Para ver un ejemplo de cómo usar WindowManager en una app de Compose, consulta la muestra de JetNews.
La creación de diseños adaptables al espacio de pantalla disponible también reduce la cantidad de manejo especial necesario para plataformas compatibles como ChromeOS y factores de forma, como tablets y dispositivos plegables.
Cuando hayas determinado las métricas del espacio disponible para tu app, convierte el tamaño sin procesar en una clase de tamaño de ventana, como se describe en Cómo usar clases de tamaño de ventana. Las clases de tamaño de ventana son puntos de interrupción diseñados para equilibrar la simplicidad de la lógica de la app con la flexibilidad de optimizarla para la mayoría de los tamaños de pantalla.
Las clases de tamaño de ventana se refieren a la ventana general de tu app. Por lo tanto, úsalas para tomar decisiones sobre el diseño que afecten el diseño general de la app. Puedes pasar las clases de tamaño de ventana como un estado o bien puedes agregar una lógica adicional para crear un estado derivado y, de esta manera, pasar los elementos componibles anidados.
@Composable fun MyApp( windowSizeClass: WindowSizeClass = currentWindowAdaptiveInfo(supportLargeAndXLargeWidth = true).windowSizeClass ) { // Decide whether to show the top app bar based on window size class. val showTopAppBar = windowSizeClass.isHeightAtLeastBreakpoint(WindowSizeClass.HEIGHT_DP_MEDIUM_LOWER_BOUND) // MyScreen logic is based on the showTopAppBar boolean flag. MyScreen( showTopAppBar = showTopAppBar, /* ... */ ) }
En lugar de dispersar la lógica del tamaño de la pantalla de la app por muchos lugares que deben mantenerse sincronizados, este enfoque en capas la limita a una sola ubicación. Una sola ubicación produce un estado, que puede pasarse de manera explícita a otros elementos componibles, como cualquier otro estado de app. Este paso de estado de forma explícita simplifica los elementos componibles individuales, ya que estos toman la clase de tamaño de ventana o una configuración especificada junto con otros datos.
Los elementos componibles, flexibles y anidados son reutilizables
Los elementos componibles son más reutilizables cuando se pueden ubicar en varias ubicaciones. Si un elemento componible se debe colocar en una ubicación específica con un tamaño específico, es poco probable que se pueda volver a usar en otros contextos. Esto también significa que los elementos componibles individuales y reutilizables deben evitar depender implícitamente de la información de tamaño de pantalla global.
Imagina un elemento componible anidado que implemente un diseño de lista-detalles, que puede mostrar un solo panel o dos paneles en paralelo:
La decisión de lista-detalles debe formar parte del diseño general de la app, por lo que se pasa desde un elemento componible a nivel del contenido:
@Composable fun AdaptivePane( showOnePane: Boolean, /* ... */ ) { if (showOnePane) { OnePane(/* ... */) } else { TwoPane(/* ... */) } }
¿Y si, en cambio, quieres que un elemento componible cambie su diseño de forma independiente según el espacio de visualización disponible, por ejemplo, una tarjeta que muestre detalles adicionales si el espacio lo permite? Quieres ejecutar una lógica según el tamaño de pantalla disponible, pero ¿en qué tamaño específicamente?
Evita usar el tamaño real de la pantalla del dispositivo. ya que no se mostrará como corresponde en diferentes tipos de pantallas ni tampoco si la app no está en pantalla completa.
Dado que el elemento componible no es un elemento de este tipo a nivel del contenido, no uses las métricas de ventana actuales directamente.
Si el componente se coloca con padding (como las inserciones) o bien si la app incluye componentes como rieles de navegación o barras de la app, la cantidad de espacio de visualización disponible para el elemento componible puede diferir de forma significativa del espacio general que está disponible para la app.
Usa el ancho por el que el elemento componible realmente se renderiza. Existen dos opciones para obtener ese ancho:
Si quieres cambiar dónde o cómo se muestra el contenido, usa una colección de modificadores o un diseño personalizado para hacer que el diseño sea responsivo. Esto podría ser tan simple como que un elemento secundario llene todo el espacio disponible o colocar elementos secundarios con varias columnas si hay suficiente espacio.
Si deseas cambiar qué es lo que muestras, usa
BoxWithConstraintscomo una alternativa más potente.BoxWithConstraintsproporciona restricciones de medición que puedes usar para llamar a distintos elementos componibles según el espacio de visualización disponible. Sin embargo, esto conlleva algunos gastos, ya queBoxWithConstraintsdifiere la composición hasta la fase de diseño, cuando se conocen estas restricciones, lo que genera que se realice más trabajo durante el diseño.
@Composable fun Card(/* ... */) { BoxWithConstraints { if (maxWidth < 400.dp) { Column { Image(/* ... */) Title(/* ... */) } } else { Row { Column { Title(/* ... */) Description(/* ... */) } Image(/* ... */) } } } }
Haz que todos los datos estén disponibles para diferentes tamaños de pantalla
Cuando implementas un elemento componible que aprovecha el espacio de visualización adicional, es posible que quieras ser eficiente y cargar los datos como un efecto secundario del tamaño de visualización actual.
Sin embargo, esto infringe los principios del flujo de datos unidireccional, en el que los datos se pueden elevar y proporcionar a los elementos componibles para que se rendericen de forma correcta. Se deben brindar suficientes datos al elemento componible para que este siempre tenga contenido suficiente para cualquier tamaño de pantalla, incluso si alguna parte del contenido no se usa en todas las instancias.
@Composable fun Card( imageUrl: String, title: String, description: String ) { BoxWithConstraints { if (maxWidth < 400.dp) { Column { Image(imageUrl) Title(title) } } else { Row { Column { Title(title) Description(description) } Image(imageUrl) } } } }
Si compilas a partir del ejemplo Card, ten en cuenta que el elemento description siempre se pasa al Card. Aunque description solo se usa cuando el ancho permite que se muestre, Card siempre requiere description, sin importar el ancho disponible.
Pasar siempre contenido suficiente simplifica los diseños adaptables, ya que los hace con menos estados y evita que haya efectos secundarios cuando se cambia de tamaño de pantalla (lo que puede ocurrir debido a un cambio en el tamaño de la ventana, la orientación, o bien si se muestra en un dispositivo plegable).
Este principio también permite preservar el estado en cambios de diseño. Si elevas la información que tal vez no se use en todos los tamaños de pantalla, puedes preservar el estado de la app a medida que cambia el tamaño del diseño.
Por ejemplo, puedes elevar una marca booleana showMore para que el estado de la app se conserve cuando el cambio de tamaño de la pantalla haga que el diseño pase de ocultar y mostrar contenido:
@Composable fun Card( imageUrl: String, title: String, description: String ) { var showMore by remember { mutableStateOf(false) } BoxWithConstraints { if (maxWidth < 400.dp) { Column { Image(imageUrl) Title(title) } } else { Row { Column { Title(title) Description( description = description, showMore = showMore, onShowMoreToggled = { newValue -> showMore = newValue } ) } Image(imageUrl) } } } }
Más información
Para obtener más información sobre los diseños adaptables en Compose, consulta los siguientes recursos:
Apps de ejemplo
- CanonicalLayouts es un repositorio de patrones de diseño comprobados que proporcionan una experiencia del usuario óptima en pantallas grandes.
- JetNews muestra cómo diseñar una app que se adapta a la IU para aprovechar el espacio de visualización disponible.
- Reply es una muestra adaptable para admitir dispositivos móviles, tablets y plegables.
- Now in Android es una app que usa diseños adaptables para admitir diferentes tamaños de pantalla.
Videos
- Cómo compilar la IU de Android para cualquier tamaño de pantalla
- Factores de forma | Android Dev Summit 2022