Progettazione carte

Questo documento illustra come seguire lo stile di Glass e implementare best practice comuni per l'interfaccia utente durante l'utilizzo di GDK.

Tema vetro

Glass applica un tema standard a Glassware, rimane coerente con il resto dell'interfaccia utente. La ha le seguenti caratteristiche:

  • Utilizza il carattere tipografico Roboto
  • Visualizza le attività a schermo intero senza barra di stato o barra delle azioni
  • Applica uno sfondo nero in tinta unita

Per applicare il tema Vetro, non dichiarare un tema nel file manifest Android.

Se hai uno stile personalizzato per alcune parti dei tuoi oggetti in vetro e vuoi usare il tema Vetro predefinito per tutto il resto, eredita da Theme.DeviceDefault con l'attributo parent:

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

Consulta la guida per gli sviluppatori Android su Stili e temi per ulteriori informazioni sulla creazione di temi.

Schede in stile vetro

La CardBuilder crea schede con formato corretto dato un insieme di proprietà. Utilizzare i layout fornita da CardBuilder.Layout quando possibile, in modo che i contenuti abbiano l'aspetto e il design di altri contenuti Vetro.

Per usare CardBuilder:

  1. Crea un'istanza di CardBuilder, utilizzando il layout che preferisci da CardBuilder.Layout
  2. Imposta le proprietà della scheda, come testo, nota a piè di pagina e timestamp.
  3. Chiama il numero CardBuilder.getView() per convertire la carta in un Android View oppure CardBuilder.getRemoteViews() per convertirlo in un RemoteViews .
  4. Utilizza l'View nelle tue attività, nei tuoi layout o in una CardScrollView oppure utilizza RemoteViews in una LiveCard.

Funzionalità comuni dell'interfaccia utente

Molti dei layout forniti da CardBuilder supportano l'interfaccia utente comune descritte di seguito. Visualizza la documentazione dei singoli layout in CardBuilder.Layout per un elenco delle funzionalità supportate da ciascun tipo di scheda.

Icona di attribuzione

L'icona di attribuzione è un'icona facoltativa di 36 × 36 pixel che viene visualizzata nell'angolo in basso a destra di una scheda e a destra del timestamp. Imposta questo icona chiamando CardBuilder.setAttributionIcon() identificare la tua applicazione, soprattutto nelle schede online, in modo che l'utente possa e vedere la fonte delle informazioni su quella scheda.

Indicatore dello stack

L'indicatore dello stack, controllato CardBuilder.showStackIndicator(), una piegatura ad angolo visualizzata nell'angolo in alto a destra di una scheda. Usa come un indicatore visivo che indichi che la tua scheda rappresenta un insieme di altre schede a cui l'utente può accedere direttamente.

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();

Layout

I seguenti esempi mostrano i layout disponibili utilizzando CardBuilder.

TEXT e TEXT_FIXED

La CardBuilder.Layout.TEXT il layout mostra il testo al vivo con un elemento mosaico di immagini sullo sfondo. Il testo si ridimensiona in modo dinamico per adattarsi al meglio di spazio disponibile. CardBuilder.Layout.TEXT_FIXED è simile ma corregge le dimensioni del testo.

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 e COLUMNS_FIXED

La CardBuilder.Layout.COLUMNS mostra un mosaico di immagini o un'icona nella lato sinistro della scheda e il testo sul lato destro. Il testo viene ridimensionato in modo dinamico in modo da soddisfare al meglio lo spazio disponibile. Per mantenere fisse le dimensioni del testo, utilizza 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

La CardBuilder.Layout.CAPTION presenta un mosaico di immagini sullo sfondo e un breve testo della didascalia allineato in fondo alla scheda. Un'icona può anche essere posizionati accanto alla didascalia per rappresentare, ad esempio, l'identità di una persona associati ai contenuti della scheda.

Figura 1: (immagine di sfondo di photoeverywhere.co.uk, ritagliata)
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

La CardBuilder.Layout.TITLE presenta un mosaico di immagini sullo sfondo con un titolo centrato e un'icona facoltativa nella parte inferiore della scheda. Questo layout viene spesso utilizzato per rappresentare i contatti o condividere obiettivi. La nota a piè di pagina e il timestamp sono non supportato su questo layout.

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

AUTHOR

Utilizza la CardBuilder.Layout.AUTHOR layout per visualizzare un messaggio o una conversazione in cui l'attenzione è concentrata sull'autore. Supporta un mosaico di immagini sullo sfondo, un'icona utilizzata come avatar dell'autore e un'intestazione e un sottotitolo in cui puoi informazioni identificative.

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();

La CardBuilder.Layout.MENU simile a un menu Glass standard. Ha un icona e titolo centrati e nota a piè di pagina facoltativa. Utilizza questo layout per schermate di conferma (passaggio da "Eliminazione" a "Eliminato" dopo che seleziona una voce di menu, ad esempio). Se hai bisogno di un menù reale, usa una nel menu opzioni standard.

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

EMBED_INSIDE

La CardBuilder.Layout.EMBED_INSIDE incorpora un XML di layout personalizzato di tua scelta nel pannello Glass standard modello di scheda. Ciò ti consente di progettare una UI personalizzata per la tua applicazione, posizione corretta per la nota a piè di pagina, il timestamp, l'icona di attribuzione e dell'indicatore dello stack, se necessario.

Dopo la chiamata CardBuilder.getView(), utilizzare findViewById() sul risultato per accedere alle visualizzazioni all'interno del layout incorporato. Allo stesso modo, se chiamata CardBuilder.getRemoteViews(), puoi manipolare le visualizzazioni del layout incorporato trasmettendone gli ID direttamente nel RemoteViews metodi di impostazione.

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

Per un esempio più dettagliato, consulta la pagina GitHub Progetto apiDemo.

ALERT

La CardBuilder.Layout.ALERT layout contiene una grande icona centrata con messaggio principale e nota a piè di pagina. Utilizza questo layout in una Da Dialog a mostrare un messaggio informativo importante, un avviso o un errore in Glassware.

L'esempio seguente mostra un'implementazione di AlertDialog e ignora il e apre le impostazioni Wi-Fi quando l'utente la tocca:

  1. Crea un corso che estenda Dialog.
  2. Crea la scheda utilizzando CardBuilder con il layout CardBuilder.Layout.ALERT e imposta la visualizzazione dei contenuti con questa scheda.
  3. (Facoltativo) Crea una GestureDetector per gestire i gesti dell'utente su questa scheda.

    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. (Facoltativo) Nella tua attività, implementa una OnClickListener per gestire eventuali flussi aggiuntivi quando l'utente tocca il pulsante. Per ulteriori informazioni avviare attività relative alle impostazioni come il Wi-Fi, controlla Avvio delle impostazioni.

  5. Chiama il costruttore AlertDialog per visualizzare la scheda di avviso.

    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();
    
            ...
        }
    }
    

Layout XML

Ecco due layout di schede di base che puoi utilizzare se la classe CardBuilder non soddisfano le tue esigenze.

Layout principale

Questo layout definisce la spaziatura interna e il piè di pagina standard di una scheda. Inserisci la tua visualizzazioni nel campo RelativeLayout vuoto.

<?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>

Layout colonna a sinistra

Definisce una colonna sinistra di 240 px e una colonna destra di 400 px sotto forma di due RelativeLayout in cui puoi includere le tue visualizzazioni.

<?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>

Dimensioni standard

Utilizza questo file insieme ai layout precedenti o ai tuoi layout per per rispettare lo stile standard del vetro. Crea questo file come res/values/dimens.xml nel tuo progetto 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>