Ya está disponible la segunda Vista previa para desarrolladores de Android 11; pruébala y comparte tus comentarios.

Cómo definir diseños en Wear

Las apps de Wear OS usan las mismas técnicas de diseño que los dispositivos portátiles Android, pero se deben diseñar con restricciones específicas. No transfieras la funcionalidad y la IU de una app para dispositivos portátiles si quieres ofrecer una buena experiencia del usuario.

Para obtener más información sobre cómo diseñar apps de calidad para wearables, lee las pautas de diseño de Wear OS.

A la hora de crear diseños para apps de Wear OS, debes tener en cuenta los dispositivos con pantallas cuadradas y redondas. Es posible que el contenido que se muestre cerca de las esquinas de la pantalla aparezca recortado en dispositivos Wear OS redondos. Por lo tanto, los diseños para pantallas cuadradas pueden tener problemas de visualización en dispositivos redondos.

Por ejemplo, la figura 1 muestra cómo se ve el siguiente diseño en pantallas cuadradas y redondas:

Figura 1: Demostración sobre cómo un diseño para pantallas cuadradas no se muestra correctamente en pantallas redondas

Por lo tanto, si usas las siguientes configuraciones para tu diseño, el texto no se mostrará correctamente en dispositivos con pantallas redondas:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <TextView
            android:id="@+id/text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/hello_square" />
    </LinearLayout>
    

Hay dos formas de solucionar este problema:

  1. Usar los diseños disponibles en la biblioteca de IU de Wear para dispositivos tanto cuadrados como redondos.
    • BoxInsetLayout: Este diseño aplica diferentes inserciones de ventana según la forma de la pantalla del dispositivo. Usa este enfoque cuando quieras usar un diseño similar en ambas formas de pantalla sin tener vistas recortadas cerca de los bordes de las pantallas redondas.
    • Diseño curvo: Usa este diseño cuando desees mostrar y manipular una lista vertical de elementos optimizados para pantallas redondas.
  2. Proporcionar recursos de diseño alternativos para dispositivos cuadrados y redondos, como se describe en la guía Cómo proporcionar recursos. Durante el tiempo de ejecución, Wear detecta la forma de la pantalla del dispositivo y carga el diseño correcto.

Para compilar un proyecto de Android Studio con esta biblioteca, asegúrate de que el paquete Extras > Google Repository esté instalado en el administrador de SDK de Android. Además, debes incluir las siguientes dependencias en el archivo build.gradle de tu módulo wear:

    dependencies {
        compile fileTree(dir: 'libs', include: ['*.jar'])
        compile 'com.android.support:wear:26.0.0'
    }
    

Usa la clase BoxInsetLayout

Figura 2: Inserciones de ventana en una pantalla redonda

La clase BoxInsetLayout de la biblioteca de IU de Wear te permite definir un diseño único que funciona tanto en pantallas cuadradas como redondas. Esta clase aplica las inserciones de ventana requeridas según la forma de la pantalla y te permite alinear fácilmente las vistas en el centro o cerca de los bordes.

Nota: La clase BoxInsetLayout reemplaza a una clase similar obsoleta de la Biblioteca de compatibilidad con dispositivos wearables.

El cuadrado gris que aparece en la figura 2 muestra el área donde BoxInsetLayout puede colocar automáticamente sus vistas secundarias en pantallas redondas después de aplicar las inserciones de ventana necesarias. Para mostrarse dentro de esta área, las vistas secundarias especifican el atributo boxedEdges con estos valores:

  • Una combinación de top, bottom, left y right. En la figura 2, por ejemplo, un valor de "left|top" coloca los bordes izquierdo y superior del campo secundario dentro del cuadrado gris.
  • En la figura 2, el valor "all" coloca todo el contenido del campo secundario dentro del cuadrado gris.

En pantallas cuadradas, las inserciones de ventana son cero y se ignora el atributo boxedEdges.

Figura 3: Definición de diseño que funciona en pantallas cuadradas y redondas

El diseño que se muestra en la figura 3 utiliza el elemento <BoxInsetLayout> y funciona en pantallas cuadradas y redondas:

    <android.support.wear.widget.BoxInsetLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        android:padding="15dp">

        <FrameLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:padding="5dp"
            app:boxedEdges="all">

            <TextView
                android:gravity="center"
                android:layout_height="wrap_content"
                android:layout_width="match_parent"
                android:text="@string/sometext"
                android:textColor="@color/black" />

            <ImageButton
                android:background="@null"
                android:layout_gravity="bottom|left"
                android:layout_height="50dp"
                android:layout_width="50dp"
                android:src="@drawable/ok" />

            <ImageButton
                android:background="@null"
                android:layout_gravity="bottom|right"
                android:layout_height="50dp"
                android:layout_width="50dp"
                android:src="@drawable/cancel" />
        </FrameLayout>
    </android.support.wear.widget.BoxInsetLayout>
    

Observa las partes del diseño marcadas en negrita:

  • android:padding="15dp"

    Esta línea asigna relleno al elemento <BoxInsetLayout>. Las inserciones de ventana en dispositivos con pantallas redondas superan los 15 dp, por lo que este relleno solo se aplica a pantallas cuadradas.

  • android:padding="5dp"

    Esta línea asigna relleno al elemento FrameLayout interno. Este relleno se aplica a las pantallas cuadradas y redondas. El relleno total entre los botones y las inserciones de ventana es 20 dp en pantallas cuadradas (15 + 5) y 5 dp en pantallas redondas.

  • app:boxedEdges="all"

    Esta línea garantiza que el elemento FrameLayout y sus elementos secundarios estén encuadrados dentro del área definida por las inserciones de ventana en pantallas redondas. Esta línea no tiene efecto en pantallas cuadradas.

Usa un diseño curvo

La clase WearableRecyclerView de la biblioteca de IU de Wear te permite optar por un diseño curvo, optimizado para pantallas redondas. Si quieres habilitar un diseño curvo para listas desplazables en tu app, consulta Cómo crear un diseño curvo.

Usa diferentes diseños para pantallas cuadradas y redondas

Un dispositivo Wear puede tener una pantalla cuadrada o redonda. Tu app debe poder admitir ambas configuraciones de dispositivos. Para ello, debes proporcionar recursos alternativos. Aplica los calificadores de recursos -round y -notround a los diseños, dimensiones y demás recursos.

Por ejemplo, considera organizar los diseños de la siguiente manera:

  • El directorio layout/ contiene diseños que funcionan tanto para relojes con pantallas redondas como cuadradas.
  • Los directorios layout-round/ y layout-notround/ contienen diseños que son específicos de la forma de una pantalla.

También puedes utilizar los directorios de recursos res/values, res/values-round y res/values-notround. Si organizas los recursos de esta manera, puedes compartir un diseño único y cambiar solo atributos específicos según el tipo de dispositivo.

Usa diferentes valores

Una forma fácil de diseñar contenido para relojes con pantallas redondas y cuadradas es utilizar values/dimens.xml y values-round/dimens.xml. Si especificas diferentes configuraciones de relleno, puedes crear el siguiente diseño con un solo archivo layout.xml y los dos archivos dimens.xml:

    <dimen name="header_start_padding">36dp</dimen>
    <dimen name="header_end_padding">22dp</dimen>
    <dimen name="list_start_padding">36dp</dimen>
    <dimen name="list_end_padding">22dp</dimen>
    
Uso de values-round/dimens.xml

Figura 4: Uso de values-round/dimens.xml

    <dimen name="header_start_padding">16dp</dimen>
    <dimen name="header_end_padding">16dp</dimen>
    <dimen name="list_start_padding">10dp</dimen>
    <dimen name="list_end_padding">10dp</dimen>
    
Uso de values/dimens.xml

Figura 5: Uso de values/dimens.xml

Debes experimentar con diferentes valores para ver qué funciona mejor.

Usa XML para compensar el mentón

Algunos relojes tienen una inserción (también conocida como "mentón") en una pantalla circular. Si no compensas ese espacio, es posible que el mentón oculte algunos de tus diseños.

Por ejemplo, puedes tener el siguiente diseño:

Diseño básico de corazón

Figura 6: Diseño básico de corazón

Este fragmento de activity_main.xml define su diseño.

    <FrameLayout
      ...>
      <android.support.wear.widget.RoundedDrawable
        android:id="@+id/androidbtn"
        android:src="@drawable/ic_android"
        .../>
       <ImageButton
        android:id="@+id/lovebtn"
        android:src="@drawable/ic_favourite"
        android:paddingTop="5dp"
        android:paddingBottom="5dp"
        android:layout_gravity="bottom"
        .../>
    </FrameLayout>
    

Si no haces nada, parte del diseño desaparecerá en el mentón.

Diseño básico de corazón

Figura 7: No se aplicó ninguna solución

Puedes usar el atributo fitsSystemWindows para configurar el relleno y así evitar el mentón. El siguiente fragmento de activity_main.xml muestra el uso de fitsSystemWindows:

    <ImageButton
      android:id="@+id/lovebtn"
      android:src="@drawable/ic_favourite"
      android:paddingTop="5dp"
      android:paddingBottom="5dp"
      android:fitsSystemWindows="true"
      .../>
    
Uso de fitsSystemWindows

Figura 8: Uso del atributo fitsSystemWindows

Ten en cuenta que se anulan los valores de relleno inferior y superior que definiste para que todo encaje en la ventana del sistema. La forma de solucionar este problema es reemplazar los valores de relleno con un elemento InsetDrawable.

Crea un archivo inset_favourite.xml para definir los valores de relleno:

    <inset
      xmlns:android="http://schemas.android.com/apk/res/android"
      android:drawable="@drawable/ic_favourite"
      android:insetTop="5dp"
      android:insetBottom="5dp" />
    

Quita el relleno de activity_main.xml:

    <ImageButton
      android:id="@+id/lovebtn"
      android:src="@drawable/inset_favourite"
      android:paddingTop="5dp"
      android:paddingBottom="5dp"
      android:fitsSystemWindows="true"
      .../>
    
Uso de InsetDrawables

Figura 9: Uso de un objeto InsetDrawables

Administra el mentón de forma programática

Si necesitas más control del que puedes lograr usando un XML de forma declarativa, ajusta tu diseño de manera programática. Para obtener el tamaño del mentón, debes adjuntar una View.OnApplyWindowInsetsListener a la vista más externa de tu diseño.

Agrega lo siguiente a MainActivity.java:

Kotlin

    private var chinSize: Int = 0

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        // find the outermost element
        findViewById<View>(R.id.outer_container).apply {
            // attach a View.OnApplyWindowInsetsListener
            setOnApplyWindowInsetsListener { v, insets ->
                chinSize = insets.systemWindowInsetBottom
                // The following line is important for inner elements which react to insets
                v.onApplyWindowInsets(insets)
                insets
            }
        }
    }
    

Java

    private int chinSize;
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // find the outermost element
        final View container = findViewById(R.id.outer_container);
        // attach a View.OnApplyWindowInsetsListener
        container.setOnApplyWindowInsetsListener(new View.OnApplyWindowInsetsListener() {
            @Override
            public WindowInsets onApplyWindowInsets(View v, WindowInsets insets) {
                chinSize = insets.getSystemWindowInsetBottom();
                // The following line is important for inner elements which react to insets
                v.onApplyWindowInsets(insets);
                return insets;
            }
        });
    }