يتناول هذا المستند كيفية اتباع نمط Glass وتنفيذ أفضل الممارسات الشائعة لواجهة المستخدم عند استخدام GDK.
مظهر Glass
يطبق Glass مظهرًا قياسيًا على Glassware، بحيث يظل متسقًا مع بقية واجهة المستخدم. تشير رسالة الأشكال البيانية السمات الشخصية التالية:
- يستخدم الخط الطباعي Roboto
- عرض الأنشطة في وضع ملء الشاشة بدون شريط حالة أو شريط إجراءات
- يتم تطبيق خلفية سوداء خالصة
لتطبيق مظهر Glass، يجب عدم تعريفه في بيان 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
كلما أمكن ذلك بحيث يبدو المحتوى
الخاص بك ويشبه أي محتوى آخر على
زجاج.
لاستخدام CardBuilder
:
- يمكنك إنشاء مثيل لـ
CardBuilder
مع منحه التنسيق الذي تريده منCardBuilder.Layout
- ضبط خصائص البطاقة، مثل النص والحاشية السفلية والطابع الزمني
- الاتصال بالرقم
CardBuilder.getView()
لتحويل البطاقة إلى جهاز AndroidView
CardBuilder.getRemoteViews()
لتحويله إلىRemoteViews
الخاص بك. - يمكنك استخدام
View
في أنشطتك أو تنسيقاتك أو فيCardScrollView
، أو استخدامRemoteViews
فيLiveCard
.
الميزات الشائعة لواجهة المستخدم
يتوافق العديد من التنسيقات التي يوفّرها 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
تصميمه وكأنه قائمة زجاج قياسية. تحتوي على
الذي يوجد في الوسط وعنوانه حاشية سفلية اختيارية. استخدام هذا التخطيط من أجل
شاشات التأكيد (الانتقال من "الحذف" إلى "المحذوف" بعد المستخدم
يحدد بند قائمة، على سبيل المثال). إذا كنت بحاجة إلى قائمة حقيقية، فيجب عليك استخدام
وقائمة الخيارات القياسية بدلاً من ذلك.
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 القياسي
نموذج البطاقة. يتيح لك هذا تصميم واجهة مستخدم مخصصة لتطبيقك
في موضع صحيح للحاشية السفلية للبطاقة، والطابع الزمني، ورمز الإسناد،
مؤشر المكدس إذا لزم الأمر.
بعد الاتصال
CardBuilder.getView()
،
استخدام
findViewById()
في النتيجة للوصول إلى طرق العرض داخل التخطيط المضمّن. وبالمثل، إذا
مكالمة
CardBuilder.getRemoteViews()
،
يمكنك معالجة طرق عرض التنسيق المضمّن من خلال تمرير أرقام التعريف الخاصة بها مباشرةً.
في
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
للاطلاع على مثال أكثر تفصيلاً، راجع GitHub مشروع ApiDemo.
ALERT
تشير رسالة الأشكال البيانية
CardBuilder.Layout.ALERT
يحتوي على أيقونة كبيرة توسيط مع
الرسالة الأساسية والحاشية السفلية. استخدم هذا التنسيق في
Dialog
إلى
تعرض رسالة إعلامية مهمة أو تحذير أو خطأ في Glassware.
يوضح المثال التالي عملية تنفيذ AlertDialog
ويرفض
بطاقة وتفتح إعدادات WiFi عندما ينقر المستخدم على البطاقة:
- أنشِئ صفًا يمتد إلى
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
للتعامل مع أي تدفقات إضافية عندما ينقر المستخدم. لمزيد من المعلومات حول بدء أنشطة الإعدادات مثل WiFi، إعدادات البدء:عليك استدعاء الدالة الإنشائية
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
فيما يلي تنسيقان أساسيان للبطاقات يمكنك استخدامهما إذا كانت فئة 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
يمكنك وضع وجهات نظرك فيها
<?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>
السمات العادية
استخدم هذا الملف إلى جانب التنسيقات السابقة أو تنسيقاتك الخاصة
للالتزام بنمط الزجاج القياسي. إنشاء هذا الملف باسم
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>