In diesem Dokument wird erläutert, wie Sie den Glass-Stil befolgen und gängige Best Practices für die Benutzeroberfläche bei der Verwendung des GDK umsetzen können.
Glass-Design
Glass wendet ein Standarddesign auf Ihre Glassware an, damit es mit dem Rest der Benutzeroberfläche konsistent bleibt. Das Design hat die folgenden Eigenschaften:
- Verwendet Roboto-Schriftart
- Zeigt Aktivitäten im Vollbildmodus ohne Statusleiste oder Aktionsleiste an
- Durchgängiger, schwarzer Hintergrund
Um das Glass-Design anzuwenden, geben Sie in Ihrem Android-Manifest kein Design an.
Wenn Sie einen benutzerdefinierten Stil für Teile Ihrer Glassware haben und das Standard-Glass-Design für alles andere verwenden möchten, übernehmen Sie Theme.DeviceDefault
mit dem Attribut parent
:
<resources>
<style name="CustomTheme" parent="@android:style/Theme.DeviceDefault">
<!-- Theme customization goes here. -->
</style>
</resources>
Weitere Informationen zum Erstellen von Designs finden Sie im Entwicklerleitfaden für Android-Entwickler.
Karten im Glasstil
Die Klasse CardBuilder
erstellt Karten im richtigen Format mit einer Reihe von Attributen. Verwende nach Möglichkeit die Layouts von CardBuilder.Layout
, damit deine Inhalte in Glass wie andere Inhalte aussehen und sich anfühlen.
Gehe so vor, wenn du CardBuilder
verwenden möchtest:
- Erstellen Sie eine Instanz von
CardBuilder
mit dem gewünschten Layout ausCardBuilder.Layout
. - Legen Sie die Eigenschaften der Karte fest, z. B. den Text, die Fußnote und den Zeitstempel.
- Rufe
CardBuilder.getView()
auf, um die Karte in ein Android-View
umzuwandeln, oderCardBuilder.getRemoteViews()
, um sie in einRemoteViews
-Objekt zu konvertieren. - Verwende
View
in deinen Aktivitäten, Layouts oder in einemCardScrollView
oderRemoteViews
in einemLiveCard
.
Häufige UI-Funktionen
Viele der von CardBuilder
bereitgestellten Layouts unterstützen die unten beschriebenen gängigen Features der Benutzeroberfläche. Eine Liste der von den einzelnen Kartentypen unterstützten Funktionen finden Sie in der Dokumentation zu den einzelnen Layouts in CardBuilder.Layout
.
Attributionssymbol
Das Attributionssymbol ist ein optionales 36 × 36 Pixel großes Symbol, das rechts unten auf der Karte und rechts neben dem Zeitstempel angezeigt wird. Legen Sie dieses Symbol fest, indem Sie CardBuilder.setAttributionIcon()
aufrufen, um Ihre Anwendung zu identifizieren, insbesondere auf Live-Karten. So können Nutzer schnell die Quelle der Informationen auf dieser Karte sehen.
Stack-Anzeige
Der Stackindikator wird von CardBuilder.showStackIndicator()
gesteuert und ist ein Eckfalten. Er wird oben rechts auf einer Karte angezeigt. Dies kann als visueller Indikator dienen, dass deine Karte ein Set aus anderen Karten ist, die der Nutzer direkt antippen kann.
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();
Layouts
Die folgenden Beispiele zeigen die Layouts, die mit CardBuilder
verfügbar sind.
TEXT
und TEXT_FIXED
Im Layout CardBuilder.Layout.TEXT
wird randloser Text mit einem optionalen Bildmosaik im Hintergrund angezeigt. Die Textgröße wird dynamisch an den verfügbaren Platz angepasst.
CardBuilder.Layout.TEXT_FIXED
ist ähnlich, aber der Text wird kleiner.
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
und COLUMNS_FIXED
Das Layout CardBuilder.Layout.COLUMNS
enthält ein Bildmosaik oder ein Symbol auf der linken Seite der Karte und Text auf der rechten Seite. Die Größe des Textes wird dynamisch an den verfügbaren Platz angepasst. Wenn Sie die Textgröße beibehalten möchten, verwenden Sie 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
Das Layout CardBuilder.Layout.CAPTION
hat ein Bildmosaik im Hintergrund und einen kurzen Untertiteltext unten auf der Karte. Ein Symbol kann auch neben dem Untertitel platziert werden, um beispielsweise die Identität einer Person darzustellen, die mit dem Inhalt der Karte verknüpft ist.
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
Im Layout CardBuilder.Layout.TITLE
befindet sich im Hintergrund ein Bildmosaik mit einem zentrierten Titel und einem optionalen Symbol auf der Karte. Dieses Layout wird häufig verwendet, um Kontakte darzustellen oder Ziele zu teilen. Fußnoten und Zeitstempel werden für dieses Layout nicht unterstützt.
View view = new CardBuilder(context, CardBuilder.Layout.TITLE)
.setText("TITLE Card")
.setIcon(R.drawable.ic_phone)
.addImage(R.drawable.beach)
.getView();
AUTHOR
Verwenden Sie das Layout CardBuilder.Layout.AUTHOR
, um eine Nachricht oder Unterhaltung anzuzeigen, bei der der Fokus auf dem Autor liegt. Es unterstützt ein Bildmosaik im Hintergrund, ein Symbol als Avatar des Autors sowie eine Überschrift und Zwischenüberschrift, in der Sie identifizierende Informationen auflisten können.
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();
MENU
Das CardBuilder.Layout.MENU
-Layout ähnelt einem standardmäßigen Glass-Menü. Sie hat ein zentriertes Symbol und einen Titel sowie eine optionale Fußnote. Verwenden Sie dieses Layout für Bestätigungsbildschirme, z. B. für den Wechsel von „Löschen“ zu „Gelöscht“, nachdem der Nutzer z. B. einen Menüpunkt ausgewählt hat. Wenn Sie ein echtes Menü benötigen, sollten Sie stattdessen ein Standardmenü mit Optionen verwenden.
View view = new CardBuilder(context, CardBuilder.Layout.MENU)
.setText("MENU layout")
.setIcon(R.drawable.ic_phone)
.setFootnote("Optional menu description")
.getView();
EMBED_INSIDE
Mit dem Layout CardBuilder.Layout.EMBED_INSIDE
wird ein eigenes benutzerdefiniertes Layout-XML in die Standardvorlage für Glass-Karten eingebettet. Auf diese Weise können Sie eine benutzerdefinierte UI für Ihre Anwendung entwerfen, aber dennoch die Fußnote, den Zeitstempel, das Zuordnungssymbol und die Stackanzeige einer Karte korrekt platzieren.
Nachdem Sie CardBuilder.getView()
aufgerufen haben, verwenden Sie findViewById()
für das Ergebnis, um auf die Ansichten in Ihrem eingebetteten Layout zuzugreifen. Wenn Sie CardBuilder.getRemoteViews()
aufrufen, können Sie die Ansichten Ihres eingebetteten Layouts ändern, indem Sie deren IDs direkt an die Setter-Methoden von RemoteViews
übergeben.
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
Ein ausführlicheres Beispiel finden Sie auf der GitHub-Seite zum ApiDemo-Projekt.
ALERT
Das Layout CardBuilder.Layout.ALERT
enthält ein großes zentriertes Symbol mit einer primären Nachricht und einer Fußnote. Verwenden Sie dieses Layout in einem Dialog
, um wichtige Informationen, Warnungen oder Fehler in Ihrer Glassware anzuzeigen.
Im folgenden Beispiel wird eine Implementierung von AlertDialog
gezeigt. Die Karte wird geschlossen und die WLAN-Einstellungen werden geöffnet, wenn der Nutzer auf die Karte tippt.
- Erstellen Sie eine Klasse, die
Dialog
erweitert. - Erstellen Sie die Karte mit
CardBuilder
und dem LayoutCardBuilder.Layout.ALERT
und legen Sie dann die Inhaltsansicht mit dieser Karte fest. Optional: Erstellen Sie ein
GestureDetector
-Element, um Nutzergesten auf dieser Karte zu verarbeiten.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); } }
Optional: Implementieren Sie in Ihrer Aktivität eine
OnClickListener
, um zusätzliche Abläufe zu verarbeiten, wenn der Nutzer tippt. Weitere Informationen zum Starten von Einstellungsaktivitäten wie WLAN finden Sie unter Starteinstellungen.Rufen Sie den Konstruktor
AlertDialog
auf, um die Benachrichtigungskarte anzuzeigen.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(); ... } }
XML-Layouts
Hier sind zwei grundlegende Kartenlayouts, die Sie verwenden können, wenn die CardBuilder-Klasse Ihren Anforderungen nicht entspricht.
Hauptlayout
Mit diesem Layout werden der Standardabstand und die Fußzeile für eine Karte definiert. Platzieren Sie Ihre eigenen Ansichten im leeren 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>
Layout der linken Spalte
Dies definiert eine linke und eine 400 px rechte Spalte in Form von zwei RelativeLayout
s, in die Sie Ihre Ansichten einfügen können.
<?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>
Standarddimensionen
Verwenden Sie diese Datei in Verbindung mit den vorherigen Layouts oder Ihren eigenen Layouts, um dem Glass-Standardstil zu entsprechen. Erstellen Sie diese Datei als res/values/dimens.xml
in Ihrem Android-Projekt.
<?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>