Desain Kartu

Dokumen ini membahas cara mengikuti gaya Glass dan menerapkan praktik terbaik UI umum saat menggunakan GDK.

Tema kaca

Glass menerapkan tema standar ke Glassware Anda, sehingga akan tetap konsisten dengan antarmuka pengguna lainnya. Tema memiliki karakteristik berikut:

  • Menggunakan jenis huruf Roboto
  • Menampilkan aktivitas layar penuh tanpa status bar atau panel tindakan
  • Menerapkan latar belakang hitam dan solid

Untuk menerapkan tema Glass, jangan deklarasikan tema di Manifes Android Anda.

Jika Anda memiliki gaya kustom untuk bagian dari Glassware dan ingin tema Glass default untuk lainnya, warisi dari Theme.DeviceDefault dengan atribut parent:

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

Lihat panduan developer Android tentang Gaya dan Tema untuk mengetahui informasi selengkapnya tentang cara membuat tema.

Kartu bergaya kaca

Class CardBuilder membuat kartu yang diformat dengan baik berdasarkan kumpulan properti. Gunakan tata letak yang disediakan oleh CardBuilder.Layout jika memungkinkan sehingga konten Anda terlihat dan terasa seperti konten lain di Kacamata.

Untuk menggunakan CardBuilder:

  1. Buat instance CardBuilder, dengan tata letak yang diinginkan dari CardBuilder.Layout.
  2. Tetapkan properti kartu, seperti teks, catatan kaki, dan stempel waktu.
  3. Panggil CardBuilder.getView() untuk mengonversi kartu ke Android View, atau CardBuilder.getRemoteViews() untuk mengonversinya menjadi objek RemoteViews.
  4. Gunakan View di aktivitas, tata letak, atau di CardScrollView, atau gunakan RemoteViews di LiveCard.

Fitur UI umum

Banyak tata letak yang disediakan oleh CardBuilder mendukung fitur antarmuka pengguna umum yang dijelaskan di bawah ini. Lihat dokumentasi setiap tata letak di CardBuilder.Layout untuk mengetahui daftar fitur yang didukung oleh setiap jenis kartu.

Ikon atribusi

Ikon atribusi adalah ikon opsional 36 × 36 piksel yang muncul di sudut kanan bawah kartu dan di sebelah kanan stempel waktu. Tetapkan ikon ini dengan memanggil CardBuilder.setAttributionIcon() untuk mengidentifikasi aplikasi Anda, terutama pada kartu live sehingga pengguna dapat dengan cepat melihat sekilas dan melihat sumber informasi pada kartu tersebut.

Indikator tumpukan

Indikator stack, yang dikontrol oleh CardBuilder.showStackIndicator(), adalah lipatan sudut yang muncul di pojok kanan atas kartu. Gunakan ini sebagai indikator visual bahwa kartu Anda mewakili paket kartu lain yang dapat diketuk langsung oleh pengguna.

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

Tata letak

Contoh berikut menunjukkan tata letak yang tersedia menggunakan CardBuilder.

TEXT dan TEXT_FIXED

Tata letak CardBuilder.Layout.TEXT menampilkan teks full-bleed dengan mosaik gambar opsional di latar belakang. Ukuran teks berubah secara dinamis agar sesuai dengan ruang yang tersedia. CardBuilder.Layout.TEXT_FIXED mirip tetapi memperbaiki teksnya ke ukuran yang lebih kecil.

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

Tata letak CardBuilder.Layout.COLUMNS menampilkan mosaik atau ikon gambar di sisi kiri kartu dan teks di sisi kanan. Teks diubah ukurannya secara dinamis agar sesuai dengan ruang yang tersedia. Agar ukuran teks tetap sama, gunakan 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

Tata letak CardBuilder.Layout.CAPTION memiliki mosaik gambar di latar belakang dan teks singkat yang diratakan di bagian bawah kartu. Ikon juga dapat ditempatkan di samping teks untuk mewakili, misalnya, identitas orang yang terkait dengan konten kartu.

Gambar 1: (gambar latar belakang oleh photoeverywhere.co.uk, dipangkas)
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

Tata letak CardBuilder.Layout.TITLE memiliki mosaik gambar di latar belakang dengan judul di tengah dan ikon opsional di bagian bawah kartu. Tata letak ini sering digunakan untuk mewakili kontak atau target berbagi. Catatan kaki dan stempel waktu tidak didukung pada tata letak ini.

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

AUTHOR

Gunakan tata letak CardBuilder.Layout.AUTHOR untuk menampilkan pesan atau percakapan yang menjadi fokus penulis. Fitur ini mendukung mosaik gambar di latar belakang, ikon yang digunakan sebagai avatar penulis, serta tajuk dan subjudul tempat Anda dapat mencantumkan informasi identitas.

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

Tata letak CardBuilder.Layout.MENU terlihat seperti menu Glass standar. Kartu memiliki ikon dan judul yang dipusatkan dan catatan kaki opsional. Gunakan tata letak ini untuk layar konfirmasi (misalnya, transisi dari "Menghapus" ke "Dihapus" setelah pengguna memilih item menu). Jika memerlukan menu sungguhan, Anda harus menggunakan menu opsi standar.

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

EMBED_INSIDE

Tata letak CardBuilder.Layout.EMBED_INSIDE menyematkan XML tata letak kustom desain Anda sendiri ke dalam template kartu Glass standar. Dengan begitu, Anda dapat mendesain UI kustom untuk aplikasi Anda, tetapi tetap memiliki penempatan catatan kaki, stempel waktu, ikon atribusi, dan indikat kartu yang benar jika diperlukan.

Setelah memanggil CardBuilder.getView(), gunakan findViewById() pada hasil untuk mengakses tampilan di dalam tata letak sematan Anda. Demikian pula, jika memanggil CardBuilder.getRemoteViews(), Anda dapat memanipulasi tampilan tata letak sematan dengan meneruskan ID-nya langsung ke metode penyetel 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

Untuk contoh yang lebih detail, lihat project ApiDemo GitHub.

ALERT

Tata letak CardBuilder.Layout.ALERT berisi ikon besar di tengah dengan pesan utama dan catatan kaki. Gunakan tata letak ini di Dialog untuk menampilkan pesan informasi, peringatan, atau error penting di Glassware Anda.

Contoh berikut menunjukkan implementasi AlertDialog dan menutup kartu serta membuka setelan Wi-Fi saat pengguna mengetuk kartu:

  1. Buat class yang memperluas Dialog.
  2. Buat kartu menggunakan CardBuilder dengan tata letak CardBuilder.Layout.ALERT, lalu setel tampilan konten dengan kartu ini.
  3. (Opsional) Buat GestureDetector untuk menangani gestur pengguna pada kartu ini.

    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. (Opsional) Dalam aktivitas Anda, terapkan OnClickListener untuk menangani alur tambahan saat pengguna mengetuk. Untuk mengetahui informasi cara memulai aktivitas setelan seperti Wi-Fi, lihat Setelan awal.

  5. Panggil konstruktor AlertDialog untuk menampilkan kartu pemberitahuan.

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

Tata letak XML

Berikut adalah dua tata letak kartu dasar yang dapat Anda gunakan jika class CardBuilder tidak memenuhi kebutuhan Anda.

Tata letak utama

Tata letak ini menentukan padding dan footer standar untuk kartu. Tempatkan tampilan Anda sendiri di RelativeLayout yang kosong.

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

Tata letak kolom kiri

Ini menentukan kolom kiri 240 px dan kolom kanan 400 px dalam bentuk dua RelativeLayout tempat Anda dapat memasukkan tampilan.

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

Dimensi standar

Gunakan file ini bersama dengan tata letak sebelumnya atau tata letak Anda sendiri untuk mematuhi gaya Glass standar. Buat file ini sebagai res/values/dimens.xml dalam project Android Anda.

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