La compatibilidad con diferentes tamaños de pantalla permite que una mayor cantidad de usuarios y una mayor variedad de dispositivos accedan a tu app.
Para admitir tantos tamaños de pantalla como sea posible, ya sean diferentes pantallas de dispositivos o 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 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 pantalla disponible. Los cambios varían desde pequeños ajustes de diseño que rellenan el espacio (diseño responsivo) hasta el reemplazo completo de un diseño por otro para que tu app pueda adaptarse mejor a los 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.
Realiza 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. Para estos tipos de elementos componibles, puede tener sentido 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, debes tomar decisiones según la parte real de la pantalla que se asigna a tu app, que 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 admitir plataformas 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 Usa 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 para optimizar tu app 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, usa las clases 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().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, /* ... */ ) }
Un enfoque en capas limita la lógica del tamaño de pantalla a una sola ubicación en lugar de dispersarla por toda la app en muchos lugares que deben mantenerse sincronizados. 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 debe colocarse en una ubicación específica con un tamaño específico, es poco probable que se pueda reutilizar 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 y 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 de 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 pantalla 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 componible a nivel del contenido, no uses las métricas de ventana actuales directamente.
Si el componente se coloca con padding (como con inserciones), o bien si la app incluye componentes como rieles de navegación o barras de la app, la cantidad de espacio de pantalla 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 pantalla 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 pantalla adicional, puede ser tentador ser eficiente y cargar los datos como un efecto secundario del tamaño de pantalla actual.
Sin embargo, esto va en contra del principio de flujo de datos unidireccional, en el que los datos se pueden elevar y proporcionar a los elementos componibles para que se rendericen de forma adecuada. Se deben proporcionar suficientes datos al elemento componible para que este siempre tenga suficiente contenido 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 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 adaptativos 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 adapta su IU para aprovechar el espacio de visualización disponible.
- Reply es un ejemplo adaptable para admitir dispositivos móviles, tablets y plegables.
- Now in Android es una app que usa diseños adaptativos 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