Projekt karty

Z tego dokumentu dowiesz się, jak korzystać ze stylu Google Glass i stosować sprawdzone metody korzystania z interfejsu GDK.

Motyw szklany

Glass stosuje standardowy motyw do Google Glass, dzięki czemu pozostaje spójny z pozostałymi interfejsami. Motyw ma takie cechy:

  • Używa kroju Roboto
  • Wyświetla aktywność na pełnym ekranie, bez paska stanu ani paska działań
  • Jednolite, czarne tło

Aby zastosować motyw Google Glass, nie zadeklaruj go w pliku manifestu Androida.

Jeśli używasz stylu niestandardowego w niektórych szklankach i chcesz mieć domyślny motyw Google Glass dla reszty produktów, odziedzicz atrybut Theme.DeviceDefault z atrybutu parent:

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

Więcej informacji o tworzeniu motywów znajdziesz w przewodniku dla programistów aplikacji na Androida dotyczącym stylów i motywów.

Karty w stylu szklanym

Klasa CardBuilder tworzy prawidłowo sformatowane karty na podstawie zestawu właściwości. Gdy tylko jest to możliwe, używaj układów udostępnianych przez CardBuilder.Layout, aby Twoje treści wyglądały i działały jak inne materiały w Szkło.

Aby użyć aplikacji CardBuilder:

  1. Utwórz instancję CardBuilder o wybranym przez siebie układzie z CardBuilder.Layout.
  2. Ustaw właściwości karty, np. tekst, przypis i sygnaturę czasową.
  3. Wywołaj CardBuilder.getView(), aby przekonwertować kartę na Androida View, lub CardBuilder.getRemoteViews(), aby przekonwertować ją na obiekt RemoteViews.
  4. Używaj View w swoich działaniach, układach lub w CardScrollView albo RemoteViews w LiveCard.

Typowe funkcje interfejsu

Wiele układów dostępnych w CardBuilder umożliwia obsługę typowych funkcji interfejsu opisanych poniżej. Listę funkcji obsługiwanych przez poszczególne typy kart znajdziesz w dokumentacji poszczególnych układów w sekcji CardBuilder.Layout.

Ikona atrybucji

Ikona atrybucji to opcjonalna ikona o wymiarach 36 × 36 pikseli, która wyświetla się w prawym dolnym rogu karty i po prawej stronie sygnatury czasowej. Ustaw tę ikonę, wywołując ikonę CardBuilder.setAttributionIcon(), aby zidentyfikować aplikację (zwłaszcza na kartach na żywo), aby użytkownik mógł szybko sprawdzić, skąd pochodzi dane karty.

Wskaźnik stosu

Wskaźnik stosu, kontrolowany przez CardBuilder.showStackIndicator(), to zagięcie narożnika widoczne w prawym górnym rogu karty. Używaj go jako wizualnego wskaźnika, który pokazuje, że karta zawiera pakiet innych kart, które użytkownik może kliknąć bezpośrednio.

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

Układy

Poniższe przykłady pokazują układy, które są dostępne za pomocą CardBuilder.

TEXTTEXT_FIXED

W układzie CardBuilder.Layout.TEXT wyświetla się pełny tekst z opcjonalną mozaiką w tle. Dynamicznie zmienia rozmiar tekstu, tak aby pasował do dostępnego miejsca. CardBuilder.Layout.TEXT_FIXED jest podobny, ale poprawia rozmiar tekstu.

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

COLUMNSCOLUMNS_FIXED

Po lewej stronie karty znajduje się mozaika lub ikona obrazu CardBuilder.Layout.COLUMNS, a po prawej – tekst. Rozmiar tekstu jest dynamicznie dostosowywany do dostępnego miejsca. Aby mieć stały rozmiar tekstu, użyj narzędzia 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

W układzie CardBuilder.Layout.CAPTION znajduje się mozaika obrazu w tle oraz krótki tekst napisów u dołu karty. Obok napisu można również umieścić ikonę reprezentującą na przykład tożsamość osoby powiązanej z treścią karty.

Ilustracja 1. (obraz tła: photoeverywhere.co.uk, przycięty)
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

W układzie CardBuilder.Layout.TITLE znajduje się mozaika w tle z wyśrodkowanym tytułem i opcjonalną ikoną u dołu karty. Ten układ jest często używany do reprezentowania kontaktów lub udostępniania celów. Przypis i sygnatura czasowa nie są obsługiwane w tym układzie.

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

AUTHOR

Użyj układu CardBuilder.Layout.AUTHOR, aby wyświetlić wiadomość lub rozmowę, w której skupiony jest autor. Obsługuje ona mozaikę w tle, ikonę używaną jako awatar autora oraz nagłówek i podtytuł, w którym można podać informacje identyfikacyjne.

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

Układ CardBuilder.Layout.MENU wygląda jak standardowe menu Glass. Ma środkową ikonę i tytuł oraz opcjonalny przypis. Ten układ służy do obsługi ekranów potwierdzenia (przejścia z „Usuwanie” na „Usunięte”, gdy użytkownik wybierze pozycję menu). Jeśli potrzebujesz prawdziwego menu, użyj standardowego menu opcji.

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

EMBED_INSIDE

W standardowym układzie karty Google Glass umieszczony jest układ CardBuilder.Layout.EMBED_INSIDE zawierający Twój własny układ XML. Dzięki temu możesz zaprojektować własny interfejs dla swojej aplikacji, ale nadal prawidłowo umieszczać w przypisach przypis, sygnaturę czasową, ikonę atrybucji i wskaźnik stosu.

Po wywołaniu elementu CardBuilder.getView() użyj wyniku findViewById(), aby uzyskać dostęp do widoków w układzie osadzonym. Podobnie, jeśli wywołujesz obiekt CardBuilder.getRemoteViews(), możesz manipulować widokami układu umieszczonego w witrynie, przekazując ich identyfikatory bezpośrednio do metod ustawiających RemoteViews.

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

Bardziej szczegółowy przykład znajdziesz w projekcie ApiDemo serwisu GitHub.

ALERT

Układ CardBuilder.Layout.ALERT zawiera dużą wyśrodkowaną ikonę z główną wiadomością i przypisem. Użyj tego układu w Dialog, aby wyświetlić ważny komunikat, ostrzeżenie lub błąd dotyczący oprogramowania Glassware.

Ten przykład pokazuje implementację tagu AlertDialog oraz zamyka kartę i otwiera ustawienia Wi-Fi, gdy użytkownik dotknie karty:

  1. Utwórz klasę, która obejmuje Dialog.
  2. Utwórz kartę za pomocą CardBuilder z układem CardBuilder.Layout.ALERT, a potem ustaw widok treści za pomocą tej karty.
  3. (Opcjonalnie) Utwórz GestureDetector do obsługi gestów na tej karcie.

    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. (Opcjonalnie) W swojej aktywności zaimplementuj OnClickListener do obsługi dodatkowych przepływów, gdy użytkownik kliknie. Więcej informacji o uruchamianiu działań związanych z ustawieniami, takich jak Wi-Fi, znajdziesz w sekcji Ustawienia uruchamiania.

  5. Wywołaj konstruktor AlertDialog, aby wyświetlić kartę alertu.

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

Układy XML

Oto 2 podstawowe układy kart, których możesz użyć, jeśli klasa CardBuilder nie spełnia Twoich potrzeb.

Układ główny

Ten układ określa standardowe dopełnienie i stopkę karty. Umieść własne widoki w pustym polu RelativeLayout.

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

Układ w lewej kolumnie

Określa ona lewą kolumnę o długości 240 i 400 pikseli w postaci dwóch kolumn RelativeLayout, w których możesz umieszczać widoki.

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

Wymiary standardowe

Aby zachować zgodność ze standardowym stylem Glass, użyj tego pliku w połączeniu z poprzednimi układami lub własnymi układami. Utwórz ten plik jako res/values/dimens.xml w projekcie Androida.

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