تصميم البطاقة

يتناول هذا المستند كيفية اتباع نمط 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:

  1. يمكنك إنشاء مثيل لـ CardBuilder مع منحه التنسيق الذي تريده من CardBuilder.Layout
  2. ضبط خصائص البطاقة، مثل النص والحاشية السفلية والطابع الزمني
  3. الاتصال بالرقم CardBuilder.getView() لتحويل البطاقة إلى جهاز Android View CardBuilder.getRemoteViews() لتحويله إلى RemoteViews الخاص بك.
  4. يمكنك استخدام 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 يحتوي التصميم على صورة فسيفساء في الخلفية ونص ترجمة موجز إلى أسفل البطاقة. يمكن أيضًا أن يكون بجانب التسمية التوضيحية لتمثيل، على سبيل المثال، هوية الشخص المرتبط بمحتوى البطاقة

الشكل 1: (صورة الخلفية بواسطة photoeverywhere.co.uk، تم اقتصاصه)
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();

تشير رسالة الأشكال البيانية 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 عندما ينقر المستخدم على البطاقة:

  1. أنشِئ صفًا يمتد إلى Dialog.
  2. يمكنك إنشاء البطاقة باستخدام CardBuilder وتنسيق CardBuilder.Layout.ALERT، ثم ضبط عرض المحتوى باستخدام هذه البطاقة.
  3. (اختياري) يمكنك إنشاء 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);
        }
    }
    
  4. (اختياري) في نشاطك، نفِّذ OnClickListener للتعامل مع أي تدفقات إضافية عندما ينقر المستخدم. لمزيد من المعلومات حول بدء أنشطة الإعدادات مثل WiFi، إعدادات البدء:

  5. عليك استدعاء الدالة الإنشائية 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>