إنشاء تطبيق مصغّر باستخدام ميزة "نظرة سريعة"

ملف البيان والبيانات الوصفية

توضّح الأقسام التالية كيفية إنشاء أداة تطبيق أساسية باستخدام Glance.

الإعلان عن AppWidget في ملف البيان

بعد إكمال خطوات الإعداد، عليك التعريف عن AppWidget وبياناته الوصفية في تطبيقك.

  1. وسِّع أداة استقبال AppWidget من GlanceAppWidgetReceiver:

    class MyAppWidgetReceiver : GlanceAppWidgetReceiver() {
        override val glanceAppWidget: GlanceAppWidget = TODO("Create GlanceAppWidget")
    }

  2. سجِّل موفِّر أداة التطبيق في ملف AndroidManifest.xml وملف البيانات الوصفية المرتبطَين:

        <receiver android:name=".glance.MyReceiver"
        android:exported="true">
        <intent-filter>
            <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
        </intent-filter>
        <meta-data
            android:name="android.appwidget.provider"
            android:resource="@xml/my_app_widget_info" />
    </receiver>
    

إضافة البيانات الوصفية AppWidgetProviderInfo

بعد ذلك، اتّبِع دليل إنشاء أداة لتحديد معلومات أداة التطبيق وإنشائها في الملف @xml/my_app_widget_info.

الفرق الوحيد في Glance هو أنّه لا يتوفّر ملف XML لـ initialLayout، ولكن عليك تحديد ملف. يمكنك استخدام تنسيق التحميل المحدّد مسبقًا والمقدَّم في المكتبة:

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/glance_default_loading_layout">
</appwidget-provider>

الإعلان عن ملف XML لـ AppWidgetProviderInfo

يحدّد العنصر AppWidgetProviderInfo الخصائص الأساسية لأداتك. حدِّد AppWidgetProviderInfo في ملف مصدر البيانات الوصفية بتنسيق XML (res/xml/my_app_widget_info.xml) داخل عنصر <appwidget-provider>

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="40dp"
    android:minHeight="40dp"
    android:targetCellWidth="1"
    android:targetCellHeight="1"
    android:maxResizeWidth="250dp"
    android:maxResizeHeight="120dp"
    android:updatePeriodMillis="86400000"
    android:description="@string/example_appwidget_description"
    android:previewLayout="@layout/example_appwidget_preview"
    android:initialLayout="@layout/glance_default_loading_layout"
    android:configure="com.example.android.ExampleAppWidgetConfigurationActivity"
    android:resizeMode="horizontal|vertical"
    android:widgetCategory="home_screen"
    android:widgetFeatures="reconfigurable|configuration_optional">
</appwidget-provider>

سمات تغيير حجم الأداة

تضع الشاشة الرئيسية التلقائية الأدوات في نافذتها استنادًا إلى شبكة من الخلايا ذات الارتفاع والعرض المحدّدَين. لا تسمح معظم الشاشات الرئيسية للأدوات إلا بأحجام تكون مضاعفات صحيحة لخلايا الشبكة، مثلاً خليتَين أفقيًا وثلاث خلايا عموديًا.

تسمح لك سمات تغيير حجم الأداة بتحديد حجم تلقائي لأداتك وتوفير حدود دنيا وعليا لحجم الأداة. في هذا السياق، يكون الحجم التلقائي للأداة هو الحجم الذي تتخذه الأداة عند إضافتها لأول مرة إلى الشاشة الرئيسية.

يصف الجدول التالي سمات <appwidget-provider> المتعلّقة بتغيير حجم الأداة:

السمات والوصف
targetCellWidth و targetCellHeight (الإصدار Android 12)، و minWidth وminHeight
  • بدءًا من الإصدار Android 12، تحدّد السمتان targetCellWidth وtargetCellHeight الحجم التلقائي للأداة من حيث خلايا الشبكة. يتم تجاهل هاتَين السمتَين في الإصدار Android 11 والإصدارات الأقدم، ويمكن تجاهلهما إذا كانت الشاشة الرئيسية لا تتيح تنسيقًا مستندًا إلى الشبكة.
  • تحدّد السمتان minWidth و minHeight الحجم التلقائي للأداة بوحدة dp. إذا لم تتطابق قيمتا الحد الأدنى لعرض الأداة أو ارتفاعها مع أبعاد الخلايا، يتم تقريب القيمتَين إلى أقرب حجم خلية.
ننصحك بتحديد كلتا مجموعتَي السمات، أي targetCellWidth و targetCellHeight، وminWidth و minHeight، حتى يتمكّن تطبيقك من الرجوع إلى استخدام minWidth وminHeight إذا كان جهاز المستخدِم لا يتيح targetCellWidth و targetCellHeight. إذا كانت السمتان targetCellWidth وtargetCellHeight متاحتَين، تكون لهما الأولوية على السمتَين minWidth وminHeight.
minResizeWidth و minResizeHeight حدِّد الحد الأدنى المطلق لحجم الأداة. تحدّد هاتان القيمتان الـ حجم الذي تصبح الأداة دونه غير قابلة للقراءة أو غير قابلة للاستخدام. يسمح استخدام هاتَين السمتَين للمستخدِم بتغيير حجم الأداة إلى حجم أصغر من الحجم التلقائي للأداة. يتم تجاهل السمة minResizeWidth إذا كانت أكبر من minWidth أو إذا لم يكُن تغيير الحجم أفقيًا مفعّلاً. راجِع resizeMode. وبالمثل، يتم تجاهل السمة minResizeHeight إذا كانت أكبر من minHeight أو إذا لم يكُن تغيير الحجم عموديًا مفعّلاً.
maxResizeWidth و maxResizeHeight حدِّد الحد الأقصى المقترَح لحجم الأداة. إذا لم تكُن القيمتان من مضاعفات أبعاد خلية الشبكة، يتم تقريبهما إلى أقرب حجم خلية. يتم تجاهل السمة maxResizeWidth إذا كانت أصغر من minWidth أو إذا لم يكُن تغيير الحجم أفقيًا مفعّلاً. راجِع resizeMode. وبالمثل، يتم تجاهل السمة maxResizeHeight إذا كانت أصغر من minHeight أو إذا لم يكُن تغيير الحجم عموديًا مفعّلاً. تم طرح هذه السمتَين في الإصدار Android 12.
resizeMode تحدّد القواعد التي يمكن بموجبها تغيير حجم الأداة. يمكنك استخدام هذه السمة لجعل أدوات الشاشة الرئيسية قابلة لتغيير الحجم أفقيًا أو عموديًا أو على كلا المحورَين. ينقر المستخدِمون مع الاستمرار على أداة لعرض مقابض تغيير الحجم، ثم يسحبون المقابض الأفقية أو العمودية لتغيير حجمها على شبكة التنسيق. تشمل قيم السمة resizeMode كلاً من horizontal وvertical وnone. للإعلان عن تطبيق مصغّر قابل لتغيير الحجم أفقيًا وعموديًا، استخدِم horizontal|vertical.

مثال

لتوضيح كيفية تأثير السمات في الجدول السابق على تغيير حجم الأداة، افترِض المواصفات التالية:

  • عرض خلية الشبكة 30 dp وارتفاعها 50 dp.
  • تم تقديم مواصفات السمات التالية:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="80dp"
    android:minHeight="80dp"
    android:targetCellWidth="2"
    android:targetCellHeight="2"
    android:minResizeWidth="40dp"
    android:minResizeHeight="40dp"
    android:maxResizeWidth="120dp"
    android:maxResizeHeight="120dp"
    android:resizeMode="horizontal|vertical" />

بدءًا من الإصدار Android 12:

استخدِم السمتَين targetCellWidth وtargetCellHeight كحجم تلقائي للأداة.

حجم الأداة هو ‎2×2 تلقائيًا. يمكن تغيير حجم الأداة إلى ‎2×1 أو ‎4×3.

الإصدار Android 11 والإصدارات الأقدم:

استخدِم السمتَين minWidth وminHeight لحساب الحجم التلقائي للأداة.

العرض التلقائي = Math.ceil(80 / 30) = 3

الارتفاع التلقائي = Math.ceil(80 / 50) = 2

حجم الأداة هو ‎3×2 تلقائيًا. يمكن تغيير حجم الأداة إلى ‎2×1 أو إلى ملء الشاشة.

سمات الأداة الإضافية

يصف الجدول التالي سمات <appwidget-provider> المتعلّقة بالخصائص الأخرى غير تغيير حجم الأداة.

السمات والوصف
updatePeriodMillis تحدّد عدد المرات التي يطلب فيها إطار عمل الأداة تحديثًا من GlanceAppWidgetReceiver من خلال استدعاء طريقة رد الاتصال onUpdate(). ننصحك بإجراء التحديثات بأقل معدل ممكن، أي مرة واحدة في الساعة على الأكثر، للحفاظ على البطارية. لمزيد من التفاصيل، راجِع قسم وقت تحديث الأدوات في إدارة حالة Glance.
initialLayout يشير إلى مصدر التنسيق الذي يحدّد تنسيق التحميل للأداة قبل عرض تركيبات واجهة مستخدم Glance. يمكنك استخدام تنسيق التحميل المحدّد مسبقًا والمقدَّم في المكتبة: @layout/glance_default_loading_layout.
configure تحدّد نشاط الإعداد الذي يتم تشغيله عندما يضيف المستخدِم الأداة. راجِع قسم تنفيذ نشاط إعداد أداة على هذه الصفحة.
description تحدّد الوصف الذي يعرضه منتقي الأدوات لأداتك. تم طرح هذه السمة في الإصدار Android 12.
previewLayout (الإصدار Android 12) وpreviewImage (الإصدار Android 11 والإصدارات الأقدم)
  • بدءًا من الإصدار Android 12، تحدّد السمة previewLayout معاينة قابلة لتغيير الحجم، والتي تقدّمها كمجموعة تنسيق XML تم ضبطها على الحجم التلقائي للأداة. من المفترض أن تشير هذه السمة إلى عملية ربط XML ثابتة تتطابق مع تنسيق تصميمك.
  • في الإصدار Android 11 أو الإصدارات الأقدم، تحدّد السمة previewImage لقطة شاشة ثابتة قابلة للرسم لما تبدو عليه الأداة، والتي تظهر في منتقي الأدوات.
ننصحك بتحديد كلتا السمتَين حتى يتمكّن تطبيقك من الرجوع إلى المنصات القديمة بسلاسة. بالنسبة إلى المنصات الأحدث (الإصدار Android 15 والإصدارات الأحدث)، يمكنك تحديد معاينات تم إنشاؤها مباشرةً في Kotlin باستخدام `GlanceAppWidget.providePreview`. راجِع دليل المعاينات التي تم إنشاؤها.
autoAdvanceViewId تحدّد معرّف العرض للعرض الفرعي للأداة الذي يتم تقدّمه تلقائيًا من قِبل مضيف الأداة.
widgetCategory تحدّد ما إذا كان يمكن عرض أداتك على الشاشة الرئيسية (home_screen) أو شاشة القفل (keyguard) أو كلتَيهما. بالنسبة إلى الإصدار Android 5.0 والإصدارات الأحدث، تكون home_screen فقط صالحة.
widgetFeatures تحدّد الميزات التي تتيحها الأداة. على سبيل المثال، إذا كان إعداد أداتك اختياريًا، حدِّد كلاً من configuration_optional وreconfigurable.

تحديد GlanceAppWidget

  1. أنشِئ فئة جديدة توسِّع نطاق GlanceAppWidget وتلغي الطريقة provideGlance. هذه هي الطريقة التي يمكنك من خلالها تحميل البيانات اللازمة لعرض أداتك:

    class MyAppWidget : GlanceAppWidget() {
    
        override suspend fun provideGlance(context: Context, id: GlanceId) {
    
            // In this method, load data needed to render the AppWidget.
            // Use `withContext` to switch to another thread for long running
            // operations.
    
            provideContent {
                // create your AppWidget here
                Text("Hello World")
            }
        }
    }

  2. أنشِئ مثيلاً له في glanceAppWidget على GlanceAppWidgetReceiver:

    class MyAppWidgetReceiver : GlanceAppWidgetReceiver() {
    
        // Let MyAppWidgetReceiver know which GlanceAppWidget to use
        override val glanceAppWidget: GlanceAppWidget = MyAppWidget()
    }

لقد أعددت الآن AppWidget باستخدام Glance.

استخدِم فئة AppWidgetProvider للتعامل مع عمليات بث الأدوات

تنسِّق GlanceAppWidgetReceiver أداة الإحداثيات عمليات بث وتعديلات حالة المنصة من خلال توسيع نطاق AppWidgetProvider الأساسي. تتلقّى هذه الفئة أحداث المنصة عند تعديل أداتك أو حذفها أو تفعيلها أو إيقافها، وتترجمها إلى طلبات دورة حياة Compose.

التعريف بتطبيق مصغّر في ملف البيان

أعلِن عن الفئة الفرعية GlanceAppWidgetReceiver كمستقبِل بث في ملف AndroidManifest.xml:

<receiver android:name="ExampleAppWidgetReceiver"
          android:exported="false">
    <intent-filter>
        <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
    </intent-filter>
    <meta-data android:name="android.appwidget.provider"
               android:resource="@xml/my_app_widget_info" />
</receiver>

يتطلّب العنصر <receiver> السمة android:name التي تحدّد فئة أداة الاستقبال. يجب أن تقبل أداة الاستقبال إجراء بث ACTION_APPWIDGET_UPDATE داخل <intent-filter>.

يجب أن يحدّد العنصر <meta-data> اسمه على أنّه android.appwidget.provider، ويجب أن تشير السمة android:resource إلى مصدر البيانات الوصفية بتنسيق XML لـ AppWidgetProviderInfo (@xml/my_app_widget_info).

تنفيذ فئة AppWidgetProvider

في Glance، يمكنك توسيع نطاق GlanceAppWidgetReceiver بدلاً من AppWidgetProvider مباشرةً. نفِّذها من خلال ربط أداة الاستقبال بمثيل GlanceAppWidget. تعمل معاودات الاتصال الأساسية المتاحة في GlanceAppWidgetReceiver على النحو التالي:

  • onUpdate(): يتم إلغاؤها تلقائيًا من قِبل Glance لتنفيذ تعديلات التركيب. إذا ألغيت onUpdate يدويًا، عليك استدعاء super.onUpdate للسماح لـ Glance بتشغيل سلاسل تعليمات التركيب بنجاح.
  • onAppWidgetOptionsChanged(): يتم استدعاؤها عند وضع الأداة لأول مرة أو تغيير حجمها. يقرأ Glance عناصر حزمة الخيارات تحت الغطاء، لذا يتم تعديل تنسيقك بسلاسة استنادًا إلى أبعاد وقت التشغيل.
  • onDeleted(Context, IntArray): يتم استدعاؤها كلما حذف المستخدِم مثيلاً معيّنًا للأداة.
  • onEnabled(Context): يتم تشغيلها عند إنشاء أول مثيل لأداتك بنجاح. هذه الطريقة ممتازة لتشغيل عمليات النقل العامة.
  • onDisabled(Context): يتم استدعاؤها عند إزالة آخر مثيل نشط للموفِّر.
  • onReceive(Context, Intent): تعترض كل عملية بث للمنصة قبل طرق معاودة الاتصال المحدّدة. عليك التأكّد من أنّ أي منطق مخصّص لأداة الاستقبال تكتبه يستدعي super.onReceive(context, intent) ويجب ألا تستدعي goAsync بنفسك أبدًا لأنّ Glance توجِّه العمل تلقائيًا بشكل غير متزامن.

تلقّي أغراض بث الأدوات

تحت الغطاء، يفلتر GlanceAppWidgetReceiver أغراض بث الأدوات الأساسية التالية للمنصة ويتعامل معها:

إنشاء واجهة المستخدم

يوضّح المقتطف التالي كيفية إنشاء واجهة المستخدم:

/* Import Glance Composables
 In the event there is a name clash with the Compose classes of the same name,
 you may rename the imports per https://kotlinlang.org/docs/packages.html#imports
 using the `as` keyword.

import androidx.glance.Button
import androidx.glance.layout.Column
import androidx.glance.layout.Row
import androidx.glance.text.Text
*/
class MyAppWidget : GlanceAppWidget() {

    override suspend fun provideGlance(context: Context, id: GlanceId) {
        // Load data needed to render the AppWidget.
        // Use `withContext` to switch to another thread for long running
        // operations.

        provideContent {
            // create your AppWidget here
            MyContent()
        }
    }

    @Composable
    private fun MyContent() {
        Column(
            modifier = GlanceModifier.fillMaxSize(),
            verticalAlignment = Alignment.Top,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp))
            Row(horizontalAlignment = Alignment.CenterHorizontally) {
                Button(
                    text = "Home",
                    onClick = actionStartActivity<MyActivity>()
                )
                Button(
                    text = "Work",
                    onClick = actionStartActivity<MyActivity>()
                )
            }
        }
    }
}

يفعل نموذج الرمز البرمجي السابق ما يلي:

  • في Column على المستوى الأعلى، يتم وضع العناصر عموديًا واحدًا تلو الآخر.
  • يوسِّع Column حجمه ليطابق المساحة المتاحة (من خلال الـ GlanceModifier ويضبط محتواه على أعلى (verticalAlignment) ويزوِّده أفقيًا (horizontalAlignment).
  • يتم تحديد محتوى Column باستخدام تعبير لامدا. الترتيب مهم.
    • العنصر الأول في Column هو مكوّن Text مع مساحة ترك مسافة داخلية تبلغ 12.dp.
    • العنصر الثاني هو Row، حيث يتم وضع العناصر أفقيًا واحدًا تلو الآخر، مع Buttons اثنين في المنتصف أفقيًا (horizontalAlignment). يعتمد العرض النهائي على المساحة المتاحة. الصورة التالية هي مثال على الشكل الذي قد تبدو عليه:
destination_widget
الشكل 1. مثال على واجهة مستخدم

يمكنك تغيير قيم المحاذاة أو تطبيق قيم معدِّلات مختلفة (مثل مساحة الترك) لتغيير موضع المكوّنات وحجمها. راجِع المستندات المرجعية للحصول على قائمة كاملة بالمكوّنات والمعلّمات والمعدِّلات المتاحة لكل فئة.

تنفيذ الزوايا المستديرة

يطرح الإصدار Android 12 معلّمات النظام لتخصيص أنصاف أقطار زوايا أدوات التطبيق ديناميكيًا:

  • system_app_widget_background_radius: تحدّد نصف قطر زاوية حاوية خلفية الأداة (لا يزيد عن 28 dp أبدًا).
  • نصف القطر الداخلي: لمنع اقتصاص المحتوى، احسب نصف قطر متناسبًا لمحتواك الداخلي استنادًا إلى المخطط التفصيلي لخلفية النظام: systemRadiusValue - widgetPadding

في Glance، يمكنك تطبيق خصائص تغيير حجم نصف القطر ديناميكيًا في التركيب باستخدام GlanceModifier.cornerRadius(android.R.dimen.system_app_widget_background_radius).

لضمان التوافق مع الأنظمة القديمة على الأجهزة التي تعمل بالإصدار Android 11 (مستوى واجهة برمجة التطبيقات 30) أو الإصدارات الأقدم، نفِّذ سمات مخصّصة وعمليات رجوع إلى مصادر المظهر المخصّصة:

  • /values/attrs.xml

    <resources>
    <attr name="backgroundRadius" format="dimension" />
    </resources>
    
  • /values/styles.xml

    <resources>
    <style name="MyWidgetTheme">
      <item name="backgroundRadius">@dimen/my_background_radius_dimen</item>
    </style>
    </resources>
    
  • /values-31/styles.xml

    <resources>
    <style name="MyWidgetTheme" parent="@android:style/Theme.DeviceDefault.DayNight">
      <item name="backgroundRadius">@android:dimen/system_app_widget_background_radius</item>
    </style>
    </resources>
    
  • /drawable/my_widget_background.xml

    <shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <corners android:radius="?attr/backgroundRadius" />
    </shape>