Tài liệu này trình bày cách làm theo kiểu Glass và triển khai các phương pháp hay nhất về giao diện người dùng phổ biến khi sử dụng GDK.
Giao diện Glass
Glass áp dụng một giao diện tiêu chuẩn cho Glassware của bạn, vì vậy Glassware nhất quán với phần còn lại của giao diện người dùng. Giao diện có các đặc điểm sau:
- Sử dụng kiểu chữ Roboto
- Hiển thị các hoạt động ở chế độ toàn màn hình không có thanh trạng thái hoặc thanh hành động
- Áp dụng nền đen đồng nhất
Để áp dụng giao diện Glass, không khai báo giao diện trong Tệp kê khai Android.
Nếu bạn có một kiểu tuỳ chỉnh cho các phần của Glassware
và muốn giao diện Glass mặc định cho mọi nội dung khác,
hãy kế thừa từ Theme.DeviceDefault
bằng thuộc tính parent
:
<resources>
<style name="CustomTheme" parent="@android:style/Theme.DeviceDefault">
<!-- Theme customization goes here. -->
</style>
</resources>
Xem hướng dẫn dành cho nhà phát triển Android về Kiểu và Giao diện để biết thêm thông tin về cách tạo giao diện.
Thẻ kiểu kính
Lớp CardBuilder
tạo các thẻ có định dạng phù hợp với một tập hợp các thuộc tính. Sử dụng các bố cục do CardBuilder.Layout
cung cấp bất cứ khi nào có thể để nội dung của bạn có giao diện như nội dung khác trên Glass.
Cách sử dụng CardBuilder
:
- Tạo một thực thể của
CardBuilder
, cung cấp cho bố cục mong muốn từCardBuilder.Layout
. - Đặt các thuộc tính của thẻ, chẳng hạn như văn bản, chú thích cuối trang và dấu thời gian.
- Gọi
CardBuilder.getView()
để chuyển đổi thẻ thành AndroidView
hoặcCardBuilder.getRemoteViews()
để chuyển đổi thành đối tượngRemoteViews
. - Sử dụng
View
trong các hoạt động, bố cục hoặc trongCardScrollView
hoặc sử dụngRemoteViews
trongLiveCard
.
Các tính năng phổ biến của giao diện người dùng
Nhiều bố cục do CardBuilder
cung cấp hỗ trợ các tính năng giao diện người dùng phổ biến như mô tả dưới đây. Hãy xem tài liệu về từng bố cục trong
CardBuilder.Layout
để biết danh sách các tính năng mà từng loại thẻ hỗ trợ.
Biểu tượng thuộc tính
Biểu tượng phân bổ là biểu tượng không bắt buộc có kích thước 36 x 36 pixel xuất hiện ở góc dưới cùng bên phải của thẻ và ở bên phải dấu thời gian. Đặt
biểu tượng này bằng cách gọi
CardBuilder.setAttributionIcon()
để xác định ứng dụng của bạn, đặc biệt là trên thẻ trực tiếp để người dùng có thể xem nhanh và xem nguồn thông tin trên thẻ đó.
Chỉ báo ngăn xếp
Chỉ báo ngăn xếp do CardBuilder.showStackIndicator()
kiểm soát là một nếp gập góc xuất hiện ở góc trên cùng bên phải của thẻ. Sử dụng thẻ này làm chỉ báo trực quan cho biết thẻ của bạn đại diện cho một gói các thẻ khác mà người dùng có thể nhấn trực tiếp vào.
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();
Bố cục
Các ví dụ sau đây cho thấy các bố cục có sẵn bằng cách sử dụng
CardBuilder
.
TEXT
và TEXT_FIXED
Bố cục CardBuilder.Layout.TEXT
hiển thị văn bản tràn lề với tranh khảm hình ảnh không bắt buộc trong nền. Văn bản sẽ tự động đổi kích thước cho vừa với không gian có sẵn.
CardBuilder.Layout.TEXT_FIXED
tương tự nhưng sửa văn bản thành kích thước nhỏ hơn.
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
và COLUMNS_FIXED
Bố cục
CardBuilder.Layout.COLUMNS
hiển thị tranh khảm hoặc biểu tượng ở
bên trái thẻ và văn bản ở bên phải. Văn bản có kích thước động
để phù hợp nhất với không gian hiện có. Để cố định kích thước văn bản, hãy sử dụng 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
Bố cục CardBuilder.Layout.CAPTION
có tranh khảm hình ảnh ở nền và văn bản phụ đề ngắn được căn chỉnh ở cuối thẻ. Bạn cũng có thể đặt một biểu tượng bên cạnh phụ đề để thể hiện, chẳng hạn như danh tính của người được liên kết với nội dung của thẻ.
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
Bố cục
CardBuilder.Layout.TITLE
có tranh khảm hình ảnh ở nền
với tiêu đề ở giữa và biểu tượng không bắt buộc ở cuối thẻ. Bố cục này thường dùng để biểu thị địa chỉ liên hệ hoặc chia sẻ mục tiêu. Bố cục này không hỗ trợ chú thích cuối trang và dấu thời gian.
View view = new CardBuilder(context, CardBuilder.Layout.TITLE)
.setText("TITLE Card")
.setIcon(R.drawable.ic_phone)
.addImage(R.drawable.beach)
.getView();
AUTHOR
Sử dụng bố cục CardBuilder.Layout.AUTHOR
để hiển thị một thông báo hoặc cuộc trò chuyện tập trung vào tác giả. Tính năng này hỗ trợ tranh ảnh trên nền, biểu tượng dùng làm hình đại diện của tác giả, tiêu đề và tiêu đề phụ để bạn có thể liệt kê thông tin nhận dạng.
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
Bố cục
CardBuilder.Layout.MENU
có dạng một trình đơn Glass tiêu chuẩn. Phần này có biểu tượng và tiêu đề ở giữa, chú thích cuối trang (không bắt buộc). Sử dụng bố cục này cho màn hình xác nhận (ví dụ: chuyển từ "Xoá" sang "Đã xoá" sau khi người dùng chọn một mục trong trình đơn. Nếu cần một trình đơn thực tế, bạn nên sử dụng trình đơn tuỳ chọn chuẩn.
View view = new CardBuilder(context, CardBuilder.Layout.MENU)
.setText("MENU layout")
.setIcon(R.drawable.ic_phone)
.setFootnote("Optional menu description")
.getView();
EMBED_INSIDE
Bố cục CardBuilder.Layout.EMBED_INSIDE
nhúng XML bố cục tuỳ chỉnh do bạn thiết kế vào mẫu thẻ Glass tiêu chuẩn. Điều này cho phép bạn thiết kế giao diện người dùng tuỳ chỉnh cho ứng dụng của mình nhưng vẫn có vị trí chính xác của chú thích cuối trang, dấu thời gian, biểu tượng phân bổ và chỉ báo ngăn xếp nếu cần.
Sau khi gọi CardBuilder.getView()
, hãy sử dụng findViewById()
trên kết quả để truy cập vào các thành phần hiển thị bên trong bố cục được nhúng. Tương tự như vậy, nếu gọi CardBuilder.getRemoteViews()
, bạn có thể thao tác với các thành phần hiển thị của bố cục được nhúng bằng cách truyền trực tiếp mã nhận dạng của thành phần đó vào các phương thức setter 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
Để biết ví dụ chi tiết hơn, hãy xem Dự án ApiDemo của GitHub.
ALERT
Bố cục
CardBuilder.Layout.ALERT
chứa biểu tượng lớn ở giữa với
thông báo chính và chú thích cuối trang. Sử dụng bố cục này trong Dialog
để hiển thị thông báo, cảnh báo hoặc lỗi thông tin quan trọng trong Glassware của bạn.
Ví dụ sau đây cho thấy cách triển khai AlertDialog
và loại bỏ thẻ, đồng thời mở phần cài đặt Wi-Fi khi người dùng nhấn vào thẻ:
- Tạo một lớp mở rộng
Dialog
. - Tạo thẻ bằng cách sử dụng
CardBuilder
với bố cụcCardBuilder.Layout.ALERT
, sau đó đặt chế độ xem nội dung bằng thẻ này. (Không bắt buộc) Tạo một
GestureDetector
để xử lý các cử chỉ của người dùng trên thẻ này.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); } }
(Không bắt buộc) Trong hoạt động của bạn, hãy triển khai một
OnClickListener
để xử lý mọi luồng bổ sung khi người dùng nhấn vào. Để biết thêm thông tin về các hoạt động cài đặt bắt đầu như Wi-Fi, hãy xem Các chế độ cài đặt bắt đầu.Gọi hàm khởi tạo
AlertDialog
để hiển thị thẻ cảnh báo.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(); ... } }
Bố cục XML
Dưới đây là hai bố cục thẻ cơ bản mà bạn có thể sử dụng nếu lớp CardBuilder không đáp ứng được nhu cầu của bạn.
Bố cục chính
Bố cục này xác định khoảng đệm và chân trang chuẩn cho một thẻ. Đặt thành phần hiển thị của riêng bạn vào RelativeLayout
trống.
<?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>
Bố cục cột bên trái
Thao tác này xác định cột bên trái 240px và cột bên phải 400px ở dạng hai RelativeLayout
mà bạn có thể đặt chế độ xem vào đó.
<?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>
Phương diện chuẩn
Sử dụng tệp này cùng với các bố cục trước đó hoặc bố cục của riêng bạn để tuân thủ kiểu Glass tiêu chuẩn. Tạo tệp này dưới dạng
res/values/dimens.xml
trong dự án Android của bạn.
<?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>