Cómo migrar tu IU a diseños responsivos

Las apps para Android deben ser compatibles con un ecosistema de factores de forma de dispositivos en constante expansión. La IU de una app debe ser responsiva a fin de adaptarse a una gran variedad de tamaños de pantalla, así como a distintas orientaciones y a otros estados de los dispositivos.

Las IU responsivas se centran en los principios de flexibilidad y continuidad.

La flexibilidad se refiere a que los diseños aprovechen de forma óptima el espacio disponible y se ajusten cuando este cambie. Los ajustes pueden adoptar muchas formas: simplemente aumentar el tamaño de una sola vista, reposicionar las vistas de modo que estén en ubicaciones más accesibles, mostrar u ocultar vistas adicionales, o una combinación de estas.

La continuidad se refiere a tener una experiencia del usuario fluida durante la transición de un tamaño de ventana a otro. Cualquier experiencia en la que esté involucrado el usuario debería continuar sin interrupciones. Debido a que un cambio de tamaño puede estar acompañado de la destrucción y recreación de toda la jerarquía de vistas, es importante que el usuario no pierda su lugar ni sus datos.

Elementos que debes evitar

Evita usar valores físicos de hardware para tomar decisiones de diseño. Puede resultar tentador tomar decisiones basadas en un valor fijo, pero, en muchos casos, estos valores no son útiles para determinar el espacio con el que puede funcionar la IU.

En las tablets, es posible que una app se ejecute en el modo multiventana, lo que significa que la app comparte la pantalla con otra. En Chrome OS, es posible que una app se ejecute en una ventana que puede cambiar de tamaño. Incluso puede haber más de una pantalla física, por ejemplo, como sucede con un dispositivo plegable o un dispositivo con varias pantallas. En todos estos casos, el tamaño físico de la pantalla no es relevante para decidir la forma en la que se mostrará el contenido.

Varios dispositivos que muestran ventanas de apps de diferentes tamaños.
Figura 1: El tamaño de las ventanas puede diferir del tamaño físico de la pantalla o del dispositivo.

Por el mismo motivo, evita bloquear tu app a una orientación o relación de aspecto específicas. Si bien el dispositivo en sí puede estar en una orientación determinada, tu app puede estar en una orientación diferente según el tamaño de la ventana. Por ejemplo, en una tablet orientada horizontalmente mientras se usa el modo multiventana, una app puede estar en modo vertical si es más alta que ancha.

Además, evita tratar de determinar si el dispositivo es un teléfono o una tablet. En particular, lo que califica como tablet es un poco subjetivo: ¿se basa en tener un determinado tamaño o relación de aspecto, o una combinación de ambos? A medida que surgen nuevos factores de forma, estas suposiciones pueden cambiar con facilidad, y esta distinción pierde importancia.

En lugar de hacer lo anterior, usa puntos de interrupción y clases de tamaño de ventana.

Puntos de interrupción y clases de tamaño de ventana

La parte real de la pantalla que se asigna a tu app es la ventana de la app. Puede ocupar la pantalla completa o parte de ella, por lo que deberás usar el tamaño de la ventana cuando tomes decisiones generales sobre el diseño de tu app.

Cuando diseñes para distintos factores de forma, busca valores umbrales donde estas decisiones de alto nivel se ramifiquen en diferentes direcciones. Para ello, los lineamientos de Material Design proporcionan puntos de interrupción en el ancho y el alto, lo que te permite asignar tamaños sin procesar a grupos discretos y estandarizados, conocidos como clases de tamaño de ventana. Debido a la ubicuidad del desplazamiento vertical, lo que más interesa en gran parte de las apps son las clases de tamaño de ancho, por lo que la mayoría de ellas pueden optimizarse para todos los tamaños de pantalla con solo controlar unos pocos puntos de interrupción. Si deseas obtener más información sobre las clases de tamaño de ventana, consulta Cómo brindar compatibilidad con diferentes tamaños de pantalla.

Elementos persistentes de la IU

Los lineamientos de diseño de Material Design definen regiones para el contenido, la navegación y las barras de la app. Por lo general, los dos últimos son elementos persistentes de la IU en la raíz de la jerarquía de vistas (o muy cerca de esta). Ten en cuenta que "persistente" no siempre significa que la vista siempre es visible, sino que permanece en su lugar, mientras que otras vistas de contenido tienen la posibilidad de moverse o cambiar. Por ejemplo, un elemento de navegación podría estar en un panel lateral deslizante que se encuentre fuera de la pantalla, pero el panel lateral siempre está allí.

Los elementos persistentes pueden ser responsivos y, por lo general, ocupan todo el ancho o todo el alto de la ventana, por lo que te recomendamos que uses clases de tamaño a fin de decidir dónde colocarlos. Esto definirá el espacio restante para el contenido. En el siguiente fragmento, la actividad usa una barra inferior para pantallas compactas y una barra superior de la aplicación para pantallas más grandes. Los diseños calificados usan puntos de interrupción de ancho como se describió antes.

<!-- res/layout/main_activity.xml -->

<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <!-- content view(s) -->

    <com.google.android.material.bottomappbar.BottomAppBar
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        ... />
</androidx.constraintlayout.widget.ConstraintLayout>

<!-- res/layout-w600dp/main_activity.xml -->
<androidx.constraintlayout.widget.ConstraintLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        ... />

    <!-- content view(s) -->
</androidx.constraintlayout.widget.ConstraintLayout>

Contenido

Una vez que hayas posicionado los elementos persistentes de la IU, usa el espacio restante para el contenido, por ejemplo, usando un NavHostFragment con el gráfico de navegación de la app. Consulta Navegación para IU responsivas a fin de obtener más aspectos a considerar.

Garantiza la disponibilidad de todos los datos para diferentes tamaños

En la actualidad, la mayoría de los frameworks de apps usan un modelo de datos independiente de los componentes de Android que contribuyen a la IU (actividades, fragmentos y vistas). Con Jetpack, esta función suele cumplirse usando ViewModels, que tienen el beneficio adicional de permanecer vigentes tras los cambios de configuración (si deseas obtener más información, consulta Descripción general de ViewModel).

Cuando implementas un diseño que se adapta a diferentes tamaños, puede resultar tentador usar un modelo de datos diferente en función del tamaño actual. Sin embargo, esto va en contra del principio de flujo unidireccional de datos. Los datos deben fluir hacia las Vistas, y los eventos como las interacciones del usuario deben fluir hacia arriba. Crear una dependencia en la dirección opuesta, en la que el modelo de datos depende de la configuración de la capa de la IU, complica las cosas de forma drástica. De esta manera, cuando la app cambie de tamaño, deberás tener en cuenta la conversión de un modelo de datos a otro.

En su lugar, deja que tu modelo de datos se adapte a la clase de tamaño más grande y, luego, puedes mostrar, ocultar o mover el contenido de forma selectiva en la IU para adaptarlo a la clase de tamaño actual. A continuación, se muestran algunas estrategias que puedes usar a la hora de decidir cómo se comportará tu diseño cuando realices la transición entre clases de tamaño.

Expande el contenido

Diseños canónicos: Feed, Hero

El espacio expandido puede ser una oportunidad para agrandar cosas y cambiar el formato de tu contenido de modo que resulte más accesible.

Agranda las colecciones. Muchas apps muestran una colección de elementos en un contenedor de desplazamiento, como RecyclerView o ScrollView. Si habilitas que se agrande un contenedor, automáticamente se podrá mostrar más contenido. Sin embargo, ten en cuenta que el contenido en él no se estirará ni se distorsionará demasiado. Por ejemplo, con un RecyclerView, considera usar un administrador de diseños diferente como GridLayoutManager, StaggeredGridLayoutManager. o FlexboxLayoutManager cuando el ancho no sea compacto.

Un dispositivo plegado y desplegado que muestra cómo los distintos administradores de diseños organizan el contenido de la app de manera diferente según la clase de tamaño de ancho.
Figura 2: Uso de diferentes LayoutManager según la clase de tamaño de ancho.

Los elementos individuales también pueden usar un tamaño o una forma diferentes a los efectos de mostrar más contenido y distinguir fácilmente sus límites.

Haz hincapié en un elemento hero. Si el diseño tiene un punto focal específico, como una imagen o un video, expándelo cuando la ventana de la app crezca a fin de mantener la atención del usuario. Se pueden reorganizar otros elementos complementarios alrededor de la vista hero o debajo de ella.

Existen muchas maneras de crear un diseño de este tipo, pero ConstraintLayout resulta particularmente adecuado para este propósito, ya que proporciona muchas formas de limitar el tamaño de una vista secundaria, incluida la aplicación de un porcentaje o una relación de aspecto, y posicionar sus elementos secundarios en relación con ellos mismos o con otros elementos de este tipo. Obtén más información sobre todas estas capacidades en Cómo crear una IU responsiva con ConstraintLayout.

Muestra contenido que se puede contraer de forma predeterminada. Cuando haya espacio disponible, expón contenido al que solo se podría acceder mediante interacción adicional por parte del usuario, como la presión, el desplazamiento o los gestos. Por ejemplo, el contenido que aparece en una interfaz con pestañas cuando está en modo compacto puede reorganizarse en columnas o una lista cuando haya más espacio disponible.

Expande los márgenes. Si el espacio es tan grande que no puedes encontrar un ajuste atractivo, incluso después de usar todo tu contenido, expande los márgenes del diseño de modo que el contenido permanezca centrado y las vistas individuales tengan tamaños naturales y espaciado entre ellas.

Como alternativa, un componente de pantalla completa puede transformarse en una IU de diálogo flotante. Esto es especialmente útil cuando ese componente requiere un enfoque exclusivo para cumplir con una tarea inmediata del usuario, como redactar un correo electrónico o crear un evento del calendario.

Teléfono estándar que muestra un diálogo en pantalla completa y teléfono plegable desplegado en el que se ve el mismo diálogo como una ventana flotante.
Figura 3: El diálogo en pantalla completa puede transformarse en un diálogo estándar de ancho medio y expandido.

Agrega contenido

Diseños canónicos: Panel complementario y vista de lista y detalles

Usa un panel complementario. Un panel complementario presenta contenido adicional o acciones contextuales relacionadas con el contenido principal, como los comentarios en un documento o los elementos de una lista de reproducción. Por lo general, se usa el tercio inferior de la pantalla para la altura expandida o el tercio final para el ancho expandido.

Una consideración importante es dónde colocar este contenido cuando no hay suficiente espacio para mostrar el panel. Estas son algunas alternativas que puedes explorar:

  • Panel lateral en el borde final mediante DrawerLayout
  • Panel lateral inferior mediante BottomSheetBehavior
  • Menú o ventana emergente a la que se puede acceder presionando un ícono de menú
Representaciones gráficas de un DrawerLayout, un BottomSheetBehavior y un menú de opciones.
Figura 4: Formas alternativas de presentar contenido adicional en un panel complementario.

Crea un diseño de doble panel. Es posible que las pantallas grandes muestren una combinación de funciones que suelen aparecer por separado en las más pequeñas. Un patrón de interacción común en muchas apps consiste en mostrar una lista de elementos, como contactos o resultados de la búsqueda, y cambiar al detalle de un elemento cuando se lo selecciona. En lugar de ampliar la lista para pantallas más grandes, usa la vista de lista y detalles a fin de mostrar ambas funciones en paralelo en un diseño de doble panel. A diferencia de un panel complementario, el panel de detalles de la vista de lista y detalles es un elemento independiente que se puede mostrar por separado en pantallas más pequeñas.

Usa el widget dedicado SlidingPaneLayout para implementar una vista de lista y detalles. Este widget calcula automáticamente si hay suficiente espacio a fin de mostrar ambos paneles juntos según el valor de layout_width especificado para los dos paneles, y cualquier espacio restante se puede distribuir con layout_weight. Cuando no hay suficiente espacio, cada panel usa el ancho completo del diseño, y el panel de detalles se desliza fuera de la pantalla, o bien sobre el panel de lista.

SlidingPaneLayout que muestra ambos paneles de un diseño de lista y detalles en un dispositivo con una pantalla ancha. SlidingPaneLayout que muestra el panel de detalles deslizándose sobre el panel de lista en un dispositivo con una pantalla angosta.
Figura 5: SlidingPaneLayout muestra dos paneles en ancho expandido y uno en ancho compacto.

Crea un diseño de doble panel contiene más detalles acerca del uso de SlidingPaneLayout. Además, ten en cuenta que este patrón puede afectar la forma en que estructuras tu gráfico de navegación. Puedes obtener más información al respecto en Navegación para IU responsivas.