เอกสารนี้จะอธิบายวิธีทําตามสไตล์ของ Glass และนําแนวทางปฏิบัติแนะนําทั่วไปเกี่ยวกับ UI ไปใช้เมื่อใช้ GDK
ธีมกระจก
Glass นําธีมมาตรฐานไปใช้กับ Glassware เพื่อให้สอดคล้องกับอินเทอร์เฟซส่วนอื่นๆ ของผู้ใช้ ธีมดังกล่าวมีลักษณะดังต่อไปนี้
- ใช้แบบอักษร Roboto
- แสดงกิจกรรมแบบเต็มหน้าจอโดยไม่มีแถบสถานะหรือแถบการทํางาน
- ใช้พื้นหลังสีดําทึบ
หากต้องการใช้ธีม Glass อย่าประกาศธีมในไฟล์ Manifest ของ Android
หากคุณมีสไตล์ที่กําหนดเองสําหรับส่วนต่างๆ ของ Glassware และต้องการธีม Glass เริ่มต้นสําหรับงานอื่นๆ ทั้งหมด ให้รับช่วงจาก Theme.DeviceDefault
ด้วยแอตทริบิวต์ parent
<resources>
<style name="CustomTheme" parent="@android:style/Theme.DeviceDefault">
<!-- Theme customization goes here. -->
</style>
</resources>
ดูข้อมูลเพิ่มเติมเกี่ยวกับการสร้างธีมได้ในคู่มือนักพัฒนาซอฟต์แวร์ Android เกี่ยวกับรูปแบบและธีม
การ์ดสไตล์กระจก
คลาส CardBuilder
จะสร้างการ์ดที่มีรูปแบบถูกต้องโดยใช้ชุดพร็อพเพอร์ตี้ ใช้เลย์เอาต์ที่ CardBuilder.Layout
มีให้ทุกครั้งที่ทําได้เพื่อให้เนื้อหามีรูปลักษณ์และความรู้สึกเหมือนกับเนื้อหาอื่นๆ ใน Glass
วิธีใช้ "CardBuilder
"
- สร้างอินสแตนซ์ของ
CardBuilder
โดยให้อยู่ในรูปแบบที่ต้องการจากCardBuilder.Layout
- ตั้งค่าคุณสมบัติของการ์ด เช่น ข้อความ เชิงอรรถ และการประทับเวลา
- เรียก
CardBuilder.getView()
เพื่อแปลงการ์ดเป็น AndroidView
หรือCardBuilder.getRemoteViews()
เพื่อแปลงเป็นออบเจ็กต์RemoteViews
- ใช้
View
ในกิจกรรม เลย์เอาต์ หรือในCardScrollView
หรือใช้RemoteViews
ในLiveCard
ฟีเจอร์ UI ทั่วไป
เลย์เอาต์จํานวนมากจาก CardBuilder
รองรับฟีเจอร์อินเทอร์เฟซผู้ใช้ทั่วไปตามที่อธิบายไว้ด้านล่าง โปรดดูเอกสารประกอบของเลย์เอาต์แต่ละรายการใน CardBuilder.Layout
เพื่อดูรายการฟีเจอร์ที่รองรับการ์ดแต่ละประเภท
ไอคอนการระบุแหล่งที่มา
ไอคอนการระบุแหล่งที่มาเป็นไอคอนที่ไม่บังคับขนาด 36 × 36 พิกเซลที่ปรากฏที่มุมขวาล่างของการ์ดและทางด้านขวาของการประทับเวลา ตั้งค่าไอคอนนี้โดยเรียกใช้ CardBuilder.setAttributionIcon()
เพื่อระบุแอปพลิเคชันของคุณ โดยเฉพาะในการ์ดแบบสดเพื่อให้ผู้ใช้ดูและแสดงแหล่งข้อมูลของบัตรนั้นได้อย่างรวดเร็ว
สัญญาณเป็นกลุ่ม
สัญญาณบอกสถานะสแต็กซึ่งควบคุมโดย CardBuilder.showStackIndicator()
คือมุมพับที่ปรากฏที่มุมขวาบนของการ์ด ใช้เป็นสัญญาณบอกสถานะว่าการ์ดของคุณแสดงถึงกลุ่มการ์ดอื่นๆ ที่ผู้ใช้แตะได้โดยตรง
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();
เลย์เอาต์
ตัวอย่างต่อไปนี้แสดงเลย์เอาต์ที่พร้อมใช้งานโดยใช้ CardBuilder
TEXT
และTEXT_FIXED
เลย์เอาต์ CardBuilder.Layout.TEXT
จะแสดงข้อความเต็มพื้นที่ซึ่งมีภาพโมเสคที่ไม่บังคับในเบื้องหลัง ข้อความจะปรับขนาดแบบไดนามิกให้พอดีกับพื้นที่ที่มี
CardBuilder.Layout.TEXT_FIXED
จะคล้ายกันแต่แก้ไขข้อความให้เล็กลง
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
และCOLUMNS_FIXED
เลย์เอาต์
CardBuilder.Layout.COLUMNS
จะแสดงภาพโมเสคหรือไอคอนภาพทางด้านซ้ายของการ์ดและข้อความทางด้านขวา ข้อความจะมีขนาดแบบไดนามิก
เพื่อให้พอดีกับพื้นที่ที่มี หากต้องการใช้ขนาดข้อความคงที่ ให้ใช้ 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
เลย์เอาต์ของ CardBuilder.Layout.CAPTION
มีภาพโมเสคในพื้นหลังและข้อความคําบรรยายวิดีโอสั้นๆ ที่กําหนดไว้ที่ด้านล่างของการ์ด นอกจากนี้ คุณยังวางไอคอนไว้ข้างคําบรรยายวิดีโอเพื่อแสดงถึงตัวตน เช่น ตัวตนของบุคคลที่เชื่อมโยงกับเนื้อหาในการ์ดได้ด้วย
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
เลย์เอาต์
CardBuilder.Layout.TITLE
จะมีภาพโมเสคในพื้นหลังโดยมีชื่ออยู่ตรงกลางและไอคอนที่ไม่บังคับที่ด้านล่างของการ์ด เลย์เอาต์นี้มักใช้เพื่อแสดงถึงรายชื่อติดต่อหรือแชร์เป้าหมาย ระบบไม่รองรับเชิงอรรถและการประทับเวลาในเลย์เอาต์นี้
View view = new CardBuilder(context, CardBuilder.Layout.TITLE)
.setText("TITLE Card")
.setIcon(R.drawable.ic_phone)
.addImage(R.drawable.beach)
.getView();
AUTHOR
ใช้เลย์เอาต์ CardBuilder.Layout.AUTHOR
เพื่อแสดงข้อความหรือการสนทนาที่โฟกัสอยู่ที่ผู้เขียน โดยรองรับภาพโมเสคในพื้นหลัง ไอคอนที่ใช้เป็นรูปโปรไฟล์ของผู้เขียน รวมถึงส่วนหัวและส่วนหัวย่อยที่คุณแสดงรายการข้อมูลการระบุได้
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
เลย์เอาต์ของ CardBuilder.Layout.MENU
มีลักษณะคล้ายกับเมนู Glass มาตรฐาน โดยมีไอคอนและชื่ออยู่ตรงกลาง รวมถึงมีเชิงอรรถ (ไม่บังคับ) ใช้เลย์เอาต์นี้สําหรับหน้าจอการยืนยัน (เช่น การเปลี่ยนจาก "การลบ" เป็น "ลบ" หลังจากผู้ใช้เลือกรายการในเมนู เป็นต้น) หากต้องการใช้เมนูจริง คุณควรใช้เมนูตัวเลือกมาตรฐานแทน
View view = new CardBuilder(context, CardBuilder.Layout.MENU)
.setText("MENU layout")
.setIcon(R.drawable.ic_phone)
.setFootnote("Optional menu description")
.getView();
EMBED_INSIDE
เลย์เอาต์ CardBuilder.Layout.EMBED_INSIDE
จะฝัง XML เลย์เอาต์ที่กําหนดเองของการออกแบบของคุณเองลงในเทมเพลตการ์ด Glass มาตรฐาน ซึ่งจะช่วยให้คุณออกแบบ UI ที่กําหนดเองสําหรับแอปพลิเคชันได้ แต่ยังคงมีตําแหน่งที่ถูกต้องของเชิงอรรถ การประทับเวลา ไอคอนการระบุแหล่งที่มา และสัญญาณบอกสถานะบัตรหากจําเป็น
หลังจากเรียก
CardBuilder.getView()
ให้ใช้
findViewById()
ในผลลัพธ์เพื่อเข้าถึงมุมมองภายในเลย์เอาต์ที่ฝัง ในทํานองเดียวกัน หากคุณเรียกใช้ CardBuilder.getRemoteViews()
คุณก็สามารถเปลี่ยนมุมมองของเลย์เอาต์ที่ฝังไว้ได้โดยส่งรหัสของตนไปยังเมธอด setRemoteViews
โดยตรง
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
ดูตัวอย่างโดยละเอียดเพิ่มเติมได้ที่โปรเจ็กต์ ApiDemo ของ GitHub
ALERT
เลย์เอาต์ CardBuilder.Layout.ALERT
จะมีไอคอนตรงกลางขนาดใหญ่พร้อมข้อความหลักและเชิงอรรถ ใช้เลย์เอาต์นี้ใน Dialog
เพื่อแสดงข้อความที่ให้ข้อมูล คําเตือน หรือข้อผิดพลาดที่สําคัญใน Glassware
ตัวอย่างต่อไปนี้แสดงการใช้ AlertDialog
และปิดการ์ดและเปิดการตั้งค่า Wi-Fi เมื่อผู้ใช้แตะการ์ด
- สร้างชั้นเรียนที่ครอบคลุม
Dialog
- สร้างการ์ดโดยใช้
CardBuilder
ที่มีเลย์เอาต์CardBuilder.Layout.ALERT
แล้วตั้งค่ามุมมองเนื้อหาด้วยการ์ดนี้ (ไม่บังคับ) สร้าง
GestureDetector
เพื่อจัดการท่าทางสัมผัสของผู้ใช้ในการ์ดนี้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); } }
(ไม่บังคับ) ในกิจกรรมของคุณ ให้ใช้
OnClickListener
เพื่อจัดการขั้นตอนเพิ่มเติมเมื่อผู้ใช้แตะ ดูข้อมูลเพิ่มเติมเกี่ยวกับการเริ่มต้นกิจกรรมการตั้งค่า เช่น Wi-Fi ได้ที่การตั้งค่าเริ่มต้นโทรหาเครื่องมือสร้าง
AlertDialog
เพื่อแสดงการ์ดการแจ้งเตือน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
ต่อไปนี้เป็นเลย์เอาต์พื้นฐาน 2 แบบที่คุณสามารถใช้ได้ในกรณีที่คลาส CardBuilder ไม่ตรงกับความต้องการของคุณ
เลย์เอาต์หลัก
เลย์เอาต์นี้จะกําหนดระยะห่างจากขอบและส่วนท้ายของมาตรฐานสําหรับการ์ด ใส่มุมมองของคุณเองใน 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>
เลย์เอาต์คอลัมน์ด้านซ้าย
คอลัมน์นี้จะแสดงคอลัมน์ด้านซ้ายขนาด 240 พิกเซลและคอลัมน์ด้านขวา 400 พิกเซลในรูปแบบของ RelativeLayout
2 คอลัมน์ที่คุณมุมมองได้
<?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>
มิติข้อมูลมาตรฐาน
ใช้ไฟล์นี้ร่วมกับเลย์เอาต์ก่อนหน้าหรือเลย์เอาต์ของคุณเองเพื่อปฏิบัติตามสไตล์ Glass มาตรฐาน สร้างไฟล์นี้เป็น res/values/dimens.xml
ในโปรเจ็กต์ 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>