La compatibilidad con diferentes tamaños de pantalla permite que una mayor cantidad de usuarios y una mayor variedad de dispositivos puedan acceder 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, los diseños de tu app deben ser responsivos y adaptables. Los diseños responsivos/adaptables proporcionan una experiencia del usuario optimizada sin importar el tamaño de visualización, lo que permite que tu app funcione en teléfonos, tablets, dispositivos plegables y ChromeOS, en orientaciones verticales y horizontales, y en configuraciones de visualización 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 en función del espacio de pantalla 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).
Se recomienda el uso de Jetpack Compose, el kit de herramientas declarativas de IU, para implementar y crear diseños adaptables a fin de renderizar contenido en función de los diferentes tamaños de pantalla.
Habilidades de Android
Ver en GitHubHaz que tu app sea adaptable
adaptive de Android para actualizar la IU de tu app de modo que se adapte a diferentes dispositivos y pantallas de 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 pantalla disponible para tu app. Para estos tipos de elementos componibles, podría 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 ventana de escritorio o en ChromeOS, es posible que una app se ejecute en una ventana que puede cambiar de tamaño. Incluso podría haber más de una pantalla física, como en 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 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 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ñadas para equilibrar la simplicidad de la lógica de la app con la flexibilidad para optimizar tu app en 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 estas clases de tamaño 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 visualización de la app por muchos lugares que deben mantenerse sincronizados, este enfoque en capas la limita a una sola ubicación. Una ubicación única produce un estado, que puede pasarse de manera explícita a otros elementos componibles, como lo harías con 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 visualización global.
Imagina un elemento componible anidado que implemente un diseño de lista-detalles, que puede mostrar uno o dos paneles en paralelo:
La decisión de lista-detalles debe formar parte del diseño general de la app, por lo que la decisión 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 pantalla disponible, por ejemplo, una tarjeta para mostrar detalles adicionales si el espacio lo permite? Quieres ejecutar una lógica según el tamaño de visualización disponible, pero ¿en qué tamaño específicamente?
Evita el uso del tamaño real de la pantalla del dispositivo, ya que no se mostrará como corresponde en varios 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 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, puedes usar 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 algunos elementos secundarios llenen todo el espacio disponible o colocar elementos secundarios con varias columnas si hay suficiente espacio.
Si deseas cambiar qué es lo que muestras, puedes usar
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 visualización adicional, puede ser tentador ser eficiente y cargar los datos como un efecto secundario del tamaño de visualización 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 procesen de forma correcta. Se deben brindar suficientes datos al elemento componible para que este siempre tenga lo necesario para mostrar contenido en cualquier tamaño de visualización, incluso si alguna parte de los datos 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 tomamos como base el ejemplo de Card, ten en cuenta que description siempre se pasa a Card. Aunque description solo se usa cuando el ancho permite que se muestre, Card siempre lo requiere, sin importar el ancho disponible.description
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 como resultado de 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 se eleva 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 como se muestra a continuación:
@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
Si deseas obtener más información sobre 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 pantalla disponible.
- Reply es una muestra adaptable para admitir dispositivos móviles, tablets y dispositivos 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