Diseño de tarjetas

En este documento, se explica cómo seguir el estilo de Glass. e implementar prácticas recomendadas comunes de IU cuando se usa el GDK.

Tema de vidrio

Glass aplica un tema estándar a tu Glassware; por lo tanto, y mantenga la coherencia con el resto de la interfaz de usuario. El el tema tiene las siguientes características:

  • Usa el tipo de letra Roboto.
  • Muestra las actividades en pantalla completa, sin barra de estado ni de acciones.
  • Aplica un fondo negro sólido

Para aplicar el tema de Glass, no declares un tema en tu manifiesto de Android.

Si tienes un estilo personalizado para partes de tus cristales y quieres el tema de Glass predeterminado para todo lo demás, hereda de Theme.DeviceDefault con el atributo parent:

<resources>
    <style name="CustomTheme" parent="@android:style/Theme.DeviceDefault">
        <!-- Theme customization goes here. -->
    </style>
</resources>

Consulta la guía para desarrolladores de Android sobre Estilos y temas para obtener más información sobre cómo crear temas.

Tarjetas con estilo de vidrio

El CardBuilder crea tarjetas con el formato correcto a partir de un conjunto de propiedades. Usa los diseños proporcionada por CardBuilder.Layout siempre que sea posible para que su contenido se vea como otro contenido en Vidrio.

Para usar CardBuilder, haz lo siguiente:

  1. Crea una instancia de CardBuilder y asígnale el diseño que quieras desde CardBuilder.Layout
  2. Establece las propiedades de la tarjeta, como el texto, la nota al pie y la marca de tiempo.
  3. Llama a CardBuilder.getView() para convertir la tarjeta en un modelo View o CardBuilder.getRemoteViews() para convertirlo en un RemoteViews .
  4. Usa View en tus actividades, diseños o en una CardScrollView o usa la RemoteViews en un LiveCard.

Funciones comunes de la IU

Muchos de los diseños que proporciona CardBuilder admiten la interfaz de usuario común. que se describen a continuación. Consulta la documentación de los diseños individuales en CardBuilder.Layout para obtener una lista de las funciones compatibles con cada tipo de tarjeta.

Ícono de atribución

El ícono de atribución es un ícono opcional de 36 × 36 píxeles que aparece en en la esquina inferior derecha de una tarjeta y a la derecha de la marca de tiempo. Establecer ícono llamando CardBuilder.setAttributionIcon() para identificar tu aplicación, especialmente con tarjetas “live”, a fin de que un usuario pueda y verás la fuente de la información de esa tarjeta.

Indicador de pila

El indicador de pila, controlado por CardBuilder.showStackIndicator(): es un pliegue de esquina que aparece en la esquina superior derecha de una tarjeta. Usar esto como un indicador visual de que tu tarjeta representa un conjunto de otras tarjetas que el al que el usuario puede acceder directamente.

View view = new CardBuilder(context, CardBuilder.Layout.TEXT)
    .setText("A stack indicator can be added to the corner of a card...")
    .setAttributionIcon(R.drawable.ic_smile)
    .showStackIndicator(true)
    .getView();

Diseños

En los siguientes ejemplos, se muestran los diseños disponibles con el CardBuilder

TEXT y TEXT_FIXED

El CardBuilder.Layout.TEXT muestra texto sin sangrado completo con una opción mosaico de imágenes en el fondo. El texto cambia de tamaño dinámicamente para ajustarse mejor al espacio disponible. CardBuilder.Layout.TEXT_FIXED es similar, pero corrige su texto a un tamaño más pequeño.

View view1 = new CardBuilder(context, CardBuilder.Layout.TEXT)
    .setText("This is the TEXT layout. The text size will adjust dynamically.")
    .setFootnote("This is the footnote")
    .setTimestamp("just now")
    .getView();
View view2 = new CardBuilder(context, CardBuilder.Layout.TEXT)
    .setText("You can also add images to the background of a TEXT card.")
    .setFootnote("This is the footnote")
    .setTimestamp("just now")
    .addImage(R.drawable.image1)
    .addImage(R.drawable.image2)
    .addImage(R.drawable.image3)
    .addImage(R.drawable.image4)
    .addImage(R.drawable.image5)
    .getView();
View view3 = new CardBuilder(context, CardBuilder.Layout.TEXT_FIXED)
    .setText("This is the TEXT_FIXED layout. The text size is always the same.")
    .setFootnote("This is the footnote")
    .setTimestamp("just now")
    .getView();

COLUMNS y COLUMNS_FIXED

El CardBuilder.Layout.COLUMNS muestra un mosaico o un ícono de imágenes en el lado izquierdo de la tarjeta y texto en el lado derecho. El tamaño del texto se establece de forma dinámica. para que se adapte mejor al espacio disponible. Para mantener fijo el tamaño del texto, usa CardBuilder.Layout.COLUMNS_FIXED

View view1 = new CardBuilder(context, CardBuilder.Layout.COLUMNS)
    .setText("This is the COLUMNS layout with dynamic text.")
    .setFootnote("This is the footnote")
    .setTimestamp("just now")
    .addImage(R.drawable.image1)
    .addImage(R.drawable.image2)
    .addImage(R.drawable.image3)
    .addImage(R.drawable.image4)
    .addImage(R.drawable.image5)
    .getView();
View view2 = new CardBuilder(context, CardBuilder.Layout.COLUMNS)
    .setText("You can even put a centered icon on a COLUMNS card instead of a mosaic.")
    .setFootnote("This is the footnote")
    .setTimestamp("just now")
    .setIcon(R.drawable.ic_wifi)
    .getView();
View view3 = new CardBuilder(context, CardBuilder.Layout.COLUMNS_FIXED)
    .setText("This is the COLUMNS_FIXED layout. The text size is always the same.")
    .setFootnote("This is the footnote")
    .setTimestamp("just now")
    .addImage(R.drawable.image1)
    .addImage(R.drawable.image2)
    .addImage(R.drawable.image3)
    .addImage(R.drawable.image4)
    .addImage(R.drawable.image5)
    .getView();

CAPTION

El CardBuilder.Layout.CAPTION el diseño tiene un mosaico de imágenes en el fondo y un breve texto de las leyendas alineado en la parte inferior de la tarjeta. Un ícono también puede ser colocado junto a la leyenda para representar, por ejemplo, la identidad de una persona asociada con el contenido de la tarjeta.

Figura 1: (imagen de fondo de photoeverywhere.co.uk, recortado)
View view1 = new CardBuilder(context, CardBuilder.Layout.CAPTION)
    .setText("The caption layout.")
    .setFootnote("This is the footnote")
    .setTimestamp("just now")
    .addImage(R.drawable.beach)
    .setAttributionIcon(R.drawable.ic_smile)
    .getView();

View view2 = new CardBuilder(context, CardBuilder.Layout.CAPTION)
    .setText("The caption layout with an icon.")
    .setFootnote("This is the footnote")
    .setTimestamp("just now")
    .addImage(R.drawable.beach)
    .setIcon(R.drawable.ic_avatar)
    .setAttributionIcon(R.drawable.ic_smile)
    .getView();

TITLE

El CardBuilder.Layout.TITLE el diseño tiene un mosaico de imágenes en el fondo con un título centrado y un ícono opcional en la parte inferior de la tarjeta. Este diseño suele usarse para representar contactos o compartir objetivos. La nota al pie y la marca de tiempo son no es compatible con este diseño.

View view = new CardBuilder(context, CardBuilder.Layout.TITLE)
    .setText("TITLE Card")
    .setIcon(R.drawable.ic_phone)
    .addImage(R.drawable.beach)
    .getView();

AUTHOR

Usa el CardBuilder.Layout.AUTHOR diseño para mostrar un mensaje o una conversación cuyo enfoque está en el autor. Admite un mosaico de imágenes en segundo plano, un icono utilizado como avatar del autor, y un encabezado y subtítulo donde puedes en una lista con la información de identificación correspondiente.

View view = new CardBuilder(context, CardBuilder.Layout.AUTHOR)
    .setText("The AUTHOR layout lets you display a message or conversation "
            + " with a focus on the author.")
    .setIcon(R.drawable.ic_avatar)
    .setHeading("Joe Lastname")
    .setSubheading("Mountain View, California")
    .setFootnote("This is the footnote")
    .setTimestamp("just now")
    .getView();

El CardBuilder.Layout.MENU luce como un menú de vidrio estándar. Tiene un el ícono y el título centrados, y una nota al pie opcional. Usar este diseño para pantallas de confirmación (transición de "Eliminado" a "Eliminado" después de que el usuario selecciona un elemento del menú, por ejemplo). Si necesitas un menú real, debes usar un el menú de opciones estándar.

View view = new CardBuilder(context, CardBuilder.Layout.MENU)
    .setText("MENU layout")
    .setIcon(R.drawable.ic_phone)
    .setFootnote("Optional menu description")
    .getView();

EMBED_INSIDE

El CardBuilder.Layout.EMBED_INSIDE Layout incorpora un XML de tu propio diseño personalizado en la aplicación Glass estándar. plantilla de tarjeta. Esto te permite diseñar una IU personalizada para tu aplicación tener la ubicación correcta de la nota al pie, la marca de tiempo, el ícono de atribución y un indicador de pila si es necesario.

Después de la llamada CardBuilder.getView(): usar findViewById() en el resultado para acceder a las vistas dentro de tu diseño incorporado. Del mismo modo, si llamar CardBuilder.getRemoteViews(): puedes manipular las vistas del diseño incorporado pasando sus ID directamente en la RemoteViews método set.

View view = new CardBuilder(context, CardBuilder.Layout.EMBED_INSIDE)
    .setEmbeddedLayout(R.layout.food_table)
    .setFootnote("Foods you tracked")
    .setTimestamp("today")
    .getView();
TextView textView1 = (TextView) view.findViewById(R.id.text_view_1);
textView1.setText("Water");
// ...and so on

Para obtener un ejemplo más detallado, consulta el repositorio Proyecto ApiDemo.

ALERT

El CardBuilder.Layout.ALERT contiene un ícono grande centrado con una mensaje principal y nota al pie. Usa este diseño en una Dialog a mostrar un mensaje informativo, una advertencia o un error importante en Glassware.

En el siguiente ejemplo, se muestra una implementación de AlertDialog y se descarta el elemento y abre la configuración de Wi-Fi cuando el usuario la toca:

  1. Crea una clase que extienda Dialog.
  2. Crea la tarjeta usando CardBuilder con el diseño CardBuilder.Layout.ALERT y, luego, configura la vista de contenido con esta tarjeta.
  3. (Opcional) Crea un GestureDetector para controlar los gestos del usuario en esta tarjeta.

    public class AlertDialog extends Dialog {
    
        private final DialogInterface.OnClickListener mOnClickListener;
        private final AudioManager mAudioManager;
        private final GestureDetector mGestureDetector;
    
        /**
         * Handles the tap gesture to call the dialog's
         * onClickListener if one is provided.
         */
        private final GestureDetector.BaseListener mBaseListener =
            new GestureDetector.BaseListener() {
    
            @Override
            public boolean onGesture(Gesture gesture) {
                if (gesture == Gesture.TAP) {
                    mAudioManager.playSoundEffect(Sounds.TAP);
                    if (mOnClickListener != null) {
                        // Since Glass dialogs do not have buttons,
                        // the index passed to onClick is always 0.
                        mOnClickListener.onClick(AlertDialog.this, 0);
                    }
                    return true;
                }
                return false;
            }
        };
    
        public AlertDialog(Context context, int iconResId,
                           int textResId, int footnoteResId,
                           DialogInterface.OnClickListener onClickListener) {
            super(context);
    
            mOnClickListener = onClickListener;
            mAudioManager =
                (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
            mGestureDetector =
                new GestureDetector(context).setBaseListener(mBaseListener);
    
            setContentView(new CardBuilder(context, CardBuilder.Layout.ALERT)
                    .setIcon(iconResId)
                    .setText(textResId)
                    .setFootnote(footnoteResId)
                    .getView());
        }
    
        /** Overridden to let the gesture detector handle a possible tap event. */
        @Override
        public boolean onGenericMotionEvent(MotionEvent event) {
            return mGestureDetector.onMotionEvent(event)
                || super.onGenericMotionEvent(event);
        }
    }
    
  4. (Opcional) En tu actividad, implementa una OnClickListener para manejar cualquier flujo adicional cuando el usuario presiona. Para obtener más información iniciar actividades de configuración, como Wi-Fi, consulta Iniciando configuración.

  5. Llama al constructor AlertDialog para mostrar la tarjeta de alerta.

    public class MyActivity extends Activity {
        ...
        private final DialogInterface.OnClickListener mOnClickListener =
                new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int button) {
                            // Open WiFi Settings
                            startActivity(new Intent(Settings.ACTION_WIFI_SETTINGS));
                        }
                };
    
        @Override
        protected void onCreate(Bundle bundle) {
            ...
    
            new AlertDialog(context, R.drawable.ic_cloud_sad_150, R.string.alert_text,
                R.string.alert_footnote_text, mOnClickListener).show();
    
            ...
        }
    }
    

Diseños XML

Aquí hay dos diseños de tarjetas básicos que puedes usar si la clase CardBuilder hace lo siguiente: que no satisfagan tus necesidades.

Diseño principal

Este diseño define el padding y el pie de página estándares de una tarjeta. Pon tu toque personal vistas en el RelativeLayout vacío.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <RelativeLayout
        android:id="@+id/body_layout"
        android:layout_width="match_parent"
        android:layout_height="@dimen/glass_card_body_height"
        android:layout_marginLeft="@dimen/glass_card_margin"
        android:layout_marginTop="@dimen/glass_card_margin"
        android:layout_marginRight="@dimen/glass_card_margin"
        tools:ignore="UselessLeaf"
        >

        <!-- Put your widgets inside this RelativeLayout. -->

    </RelativeLayout>

    <LinearLayout
        android:id="@+id/footer_container"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|left"
        android:layout_marginLeft="@dimen/glass_card_margin"
        android:layout_marginBottom="@dimen/glass_card_footer_margin"
        android:layout_marginRight="@dimen/glass_card_margin"
        android:orientation="horizontal"
        >

        <!-- The footer view will grow to fit as much content as possible while the
             timestamp view keeps a fixed width. If the footer text is too long, it
             will be ellipsized with a 40px margin between it and the timestamp. -->

        <TextView
            android:id="@+id/footer"
            android:layout_width="0dip"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:ellipsize="end"
            android:singleLine="true"
            android:textAppearance="?android:attr/textAppearanceSmall"
            />

        <TextView
            android:id="@+id/timestamp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="@dimen/glass_card_margin"
            android:ellipsize="end"
            android:singleLine="true"
            android:textAppearance="?android:attr/textAppearanceSmall"
            />

    </LinearLayout>

</FrameLayout>

Diseño de la columna izquierda

Esto define una columna izquierda de 240 px y una columna derecha de 400 px en forma de dos RelativeLayout. en las que puedes ingresar tus vistas.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
    >

    <RelativeLayout
        android:id="@+id/left_column"
        android:layout_width="@dimen/glass_card_left_column_width"
        android:layout_height="match_parent"
        >

        <!-- Put widgets for the left column inside this RelativeLayout. -->

    </RelativeLayout>

    <RelativeLayout
        android:layout_width="wrap_content"
        android:layout_height="@dimen/glass_card_body_height"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="@dimen/glass_card_two_column_margin"
        android:layout_marginRight="@dimen/glass_card_margin"
        android:layout_marginTop="@dimen/glass_card_margin"
        android:layout_toRightOf="@+id/left_column"
        tools:ignore="UselessLeaf"
        >

        <!-- Put widgets for the right column inside this RelativeLayout. -->

    </RelativeLayout>

    <LinearLayout
        android:id="@+id/footer_container"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_gravity="bottom|left"
        android:layout_marginBottom="@dimen/glass_card_footer_margin"
        android:layout_marginLeft="@dimen/glass_card_two_column_margin"
        android:layout_marginRight="@dimen/glass_card_margin"
        android:layout_toRightOf="@+id/left_column"
        android:orientation="horizontal"
        >

        <!--
             The footer view will grow to fit as much content as possible while the
             timestamp view keeps a fixed width. If the footer text is too long, it
             will be ellipsized with a 40px margin between it and the timestamp.
        -->

        <TextView
            android:id="@+id/footer"
            android:layout_width="0dip"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:ellipsize="end"
            android:singleLine="true"
            android:textAppearance="?android:attr/textAppearanceSmall"
            />

        <TextView
            android:id="@+id/timestamp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="@dimen/glass_card_margin"
            android:ellipsize="end"
            android:singleLine="true"
            android:textAppearance="?android:attr/textAppearanceSmall"
            />

    </LinearLayout>

</RelativeLayout>

Dimensiones estándar

Usa este archivo junto con los diseños anteriores o con tus propios diseños para para que se adhiera al estilo estándar de Glass. Crear este archivo como res/values/dimens.xml en tu proyecto de Android.

<?xml version="1.0" encoding="utf-8"?>

<resources>

    <!-- The recommended margin for the top, left, and right edges of a card. -->
    <dimen name="glass_card_margin">40px</dimen>

    <!-- The recommended margin between the bottom of the card and the footer. This is
         an adjusted value so that the baseline of the text in the footer sits 40px
         from the bottom of the card, matching the other margins. -->
    <dimen name="glass_card_footer_margin">33px</dimen>

    <!-- The recommended margin for the left column of the two-column card. -->
    <dimen name="glass_card_two_column_margin">30px</dimen>

    <!-- The maximum height of the body content inside a card. -->
    <dimen name="glass_card_body_height">240px</dimen>

    <!-- The width of the left column in the two-column layout. -->
    <dimen name="glass_card_left_column_width">240px</dimen>

</resources>