توضّح هذه الصفحة الممارسات المقترَحة لإنشاء تطبيق مصغّر أكثر تقدّمًا لتوفير تجربة أفضل للمستخدم.
تحسينات لتعديل محتوى التطبيقات المصغّرة
قد يكون تعديل محتوى التطبيقات المصغّرة ذا تكلفة حسابية عالية. لتوفير شحن البطارية، عليك تحسين نوع التحديث ومعدّل تكراره وتوقيته.
أنواع تحديثات التطبيقات المصغّرة
هناك ثلاث طرق لتعديل التطبيق المصغّر: تعديل كامل وتعديل جزئي، وفي حال تطبيق مصغّر للمجموعة، تحديث البيانات. ولكل منها تكاليف حسابية ونتائج مختلفة.
يوضّح ما يلي كل نوع من أنواع التعديلات ويقدّم مقتطفات رمز لكل نوع.
التحديث الكامل: اتصل على
AppWidgetManager.updateAppWidget(int, android.widget.RemoteViews)
لتحديث الأداة بالكامل. سيؤدي ذلك إلى استبدالRemoteViews
المقدَّمة سابقًا بملفRemoteViews
جديد. هذا هو التعديل الأكثر تكلفة من حيث العمليات الحسابية.Kotlin
val appWidgetManager = AppWidgetManager.getInstance(context) val remoteViews = RemoteViews(context.getPackageName(), R.layout.widgetlayout).also { setTextViewText(R.id.textview_widget_layout1, "Updated text1") setTextViewText(R.id.textview_widget_layout2, "Updated text2") } appWidgetManager.updateAppWidget(appWidgetId, remoteViews)
Java
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widgetlayout); remoteViews.setTextViewText(R.id.textview_widget_layout1, "Updated text1"); remoteViews.setTextViewText(R.id.textview_widget_layout2, "Updated text2"); appWidgetManager.updateAppWidget(appWidgetId, remoteViews);
تعديل جزئي: استخدِم الإجراء
AppWidgetManager.partiallyUpdateAppWidget
لتعديل أجزاء من التطبيق المصغّر. يؤدي ذلك إلى دمجRemoteViews
الجديد معRemoteViews
الذي سبق تقديمه. يتم تجاهل هذه الطريقة إذا لم تتلقّى أداة مصغّرة تحديثًا كاملاً واحدًا على الأقل من خلالupdateAppWidget(int[], RemoteViews)
.Kotlin
val appWidgetManager = AppWidgetManager.getInstance(context) val remoteViews = RemoteViews(context.getPackageName(), R.layout.widgetlayout).also { setTextViewText(R.id.textview_widget_layout, "Updated text") } appWidgetManager.partiallyUpdateAppWidget(appWidgetId, remoteViews)
Java
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widgetlayout); remoteViews.setTextViewText(R.id.textview_widget_layout, "Updated text"); appWidgetManager.partiallyUpdateAppWidget(appWidgetId, remoteViews);
إعادة تحميل بيانات المجموعة: يمكنك استدعاء الإجراء
AppWidgetManager.notifyAppWidgetViewDataChanged
لإبطال بيانات عرض المجموعة في التطبيق المصغّر. يؤدي ذلك إلى بدءRemoteViewsFactory.onDataSetChanged
. وفي هذه الأثناء، يتم عرض البيانات القديمة في التطبيق المصغّر. يمكنك استخدام هذه الطريقة لتنفيذ المهام المُكلّفة بشكل متزامن وبأمان.Kotlin
val appWidgetManager = AppWidgetManager.getInstance(context) appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_listview)
Java
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); appWidgetManager.notifyAppWidgetViewDataChanged(appWidgetId, R.id.widget_listview);
يمكنك استدعاء هذه الطرق من أي مكان في تطبيقك، شرط أن يكون للتطبيق معرّف مستخدم موحّد مماثلاً لتلك التي تخصّ فئة
AppWidgetProvider
.
تحديد عدد المرات التي يتم فيها تعديل التطبيق المصغّر
يتم تعديل التطبيقات المصغّرة بشكل دوري استنادًا إلى القيمة المقدَّمة لسمة
updatePeriodMillis
. يمكن أن يتم تعديل التطبيق المصغّر استجابةً للتفاعل مع المستخدم أو بث
التحديثات أو كليهما.
تعديلها بصفة دورية
يمكنك التحكّم في معدّل تكرار التحديث الدوري من خلال تحديد قيمة لسمة
AppWidgetProviderInfo.updatePeriodMillis
في ملف XML الخاص بـ appwidget-provider
. يؤدي كل
تعديل إلى تنشيط الطريقة AppWidgetProvider.onUpdate()
، وهي الطريقة التي
يمكنك من خلالها وضع الرمز لتحديث التطبيق المصغّر. ومع ذلك، ننصحك بالاطّلاع على البدائل ل
تحديثات مستقبلي البث الموضّحة في القسم التالي إذا كان التطبيق المصغّر يحتاج إلى تحميل البيانات بشكل غير متزامن أو يستغرق أكثر من
10 ثوانٍ لإجراء التحديث، لأنّ النظام يعتبر بعد 10 ثوانٍ أنّه
BroadcastReceiver
لا يستجيب.
لا تتيح الدالة updatePeriodMillis
استخدام قيم أقل من 30 دقيقة. ومع ذلك، إذا
أردت إيقاف التحديثات الدورية، يمكنك تحديد 0.
يمكنك السماح للمستخدمين بضبط وتيرة التحديثات في أحد الإعدادات. على سبيل المثال، قد يريدون تعديل مؤشر الأسهم كل 15 دقيقة أو أربع مرات فقط في اليوم. في هذه الحالة، اضبط updatePeriodMillis
على 0 واستخدِم
WorkManager
بدلاً من ذلك.
تعديل استجابةً لتفاعل أحد المستخدِمين
في ما يلي بعض الطرق المقترَحة لتعديل التطبيق المصغّر استنادًا إلى تفاعل المستخدم:
من نشاط في التطبيق: يمكنك استدعاء
AppWidgetManager.updateAppWidget
مباشرةً استجابةً لتفاعل المستخدم، مثل انقر على المستخدم.من التفاعلات عن بُعد، مثل إشعار أو تطبيق مصغّر: أنشئ
PendingIntent
، ثم عدِّل التطبيق المصغّر منActivity
أوBroadcast
أوService
التي تمّ استدعاؤها. يمكنك اختيار الأولوية التي تريدها. على سبيل المثال، إذا اخترتBroadcast
للعنصرPendingIntent
، يمكنك اختيار بث في المقدّمة لمنحBroadcastReceiver
الأولوية.
تعديل المحتوى استجابةً لحدث بث
على سبيل المثال، عندما يلتقط المستخدم صورة، يكون ذلك حدث بث يتطلّب تحديث التطبيق المصغّر. في هذه الحالة، تريد تعديل التطبيق المصغّر عند رصد صورة جديدة.
يمكنك جدولة مهمة باستخدام JobScheduler
وتحديد بثّ كمشغِّل
باستخدام الأسلوب
JobInfo.Builder.addTriggerContentUri
.
يمكنك أيضًا تسجيل BroadcastReceiver
للبث، على سبيل المثال،
الاستماع إلى
ACTION_LOCALE_CHANGED
.
ومع ذلك، بما أنّ هذه الميزة تستهلك موارد الجهاز، يُرجى استخدامها بحذر والاستماع
إلى البث المحدّد فقط. مع إدخال قيود
البث في Android
7.0 (المستوى 24 من واجهة برمجة التطبيقات) وAndroid 8.0 (المستوى 26 من واجهة برمجة التطبيقات)، لا يمكن للتطبيقات تسجيل البث
الضمني في بياناتها، مع بعض
الاستثناءات.
الاعتبارات الواجب مراعاتها عند تعديل تطبيق مصغّر من BroadcastReceiver
إذا تم تعديل التطبيق المصغّر من BroadcastReceiver
، بما في ذلك
AppWidgetProvider
، يجب مراعاة النقاط التالية بشأن
مدة تعديل التطبيق المصغّر وأولويته.
مدة التحديث
وكقاعدة عامة، يسمح النظام لأجهزة استقبال البث، التي تعمل عادةً في سلسلت الرسائل الأساسية للتطبيق، بالعمل لمدة تصل إلى 10 ثوانٍ قبل اعتبارها غير مستجيبة والتسبب في خطأ "التطبيق لا يستجيب". إذا استغرقت عملية تعديل التطبيقات المصغّرة وقتًا أطول، ننصحك باتّباع الخطوات التالية:
حدِّد موعدًا لمهمة باستخدام
WorkManager
.امنح المستلِم المزيد من الوقت باستخدام
goAsync
. يتيح ذلك للمستلمين تنفيذ الإجراء لمدة 30 ثانية.
يمكنك الاطّلاع على الاعتبارات المتعلّقة بالأمان وأفضل الممارسات للحصول على مزيد من المعلومات.
أولوية التحديث
يتم تشغيل عمليات البث، بما في ذلك تلك التي يتم إجراؤها باستخدام
AppWidgetProvider.onUpdate
، كعمليات في الخلفية تلقائيًا. وهذا يعني أنّه
يمكن أن تؤدي موارد النظام المثقلة بالتحميل إلى تأخير في استدعاء معالج
البث. لإعطاء الأولوية للبث، اجعله عملية في المقدّمة.
على سبيل المثال، أضِف العلامة
Intent.FLAG_RECEIVER_FOREGROUND
إلى Intent
التي تم تمريرها إلى PendingIntent.getBroadcast
عندما يضغط المستخدم
على جزء معيّن من التطبيق المصغّر.
إنشاء معاينات دقيقة تتضمّن عناصر ديناميكية
يوضّح هذا القسم المنهجية المقترَحة لعرض عناصر متعدّدة في
معاينة التطبيق المصغّر لأداة تتضمّن عرضًا
للمجموعة، أي أداة تستخدم
ListView
أو GridView
أو StackView
.
إذا كانت الأداة تستخدم أحد هذين العرضَين، يؤدي إنشاء معاينة قابلة للتوسيع من خلال تقديم تنسيق الأداة الفعلي مباشرةً إلى خفض مستوى التجربة عندما لا تعرض معاينة الأداة أي عناصر. يحدث ذلك لأنّه يتم ضبط بيانات عرض المجموعة بشكل ديناميكي أثناء التشغيل، وتبدو مشابهة لل الصورة الموضّحة في الشكل 1.
لعرض معاينات التطبيقات المصغّرة التي تتضمّن طرق عرض مجموعات بشكل صحيح في أداة اختيار التطبيقات المصغّرة، ننصحك بالاحتفاظ بملف تنسيق منفصل مخصّص للمعاينة فقط. يتضمّن ملف التنسيق المنفصل هذا تنسيق التطبيق المصغّر الفعلي وملف عرض ملف بديل
يتضمّن عناصر مزيّفة. على سبيل المثال، يمكنك تقليد
ListView
من خلال تقديم عنصر نائب LinearLayout
مع عدة عناصر
قائمة مزيّفة.
لتوضيح مثال على ListView
، ابدأ بملف تنسيق منفصل:
// res/layout/widget_preview.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/widget_background"
android:orientation="vertical">
// Include the actual widget layout that contains ListView.
<include
layout="@layout/widget_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
// The number of fake items you include depends on the values you provide
// for minHeight or targetCellHeight in the AppWidgetProviderInfo
// definition.
<TextView android:text="@string/fake_item1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="?attr/appWidgetInternalPadding" />
<TextView android:text="@string/fake_item2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginVertical="?attr/appWidgetInternalPadding" />
</LinearLayout>
حدِّد ملف تنسيق المعاينة عند تقديم سمة previewLayout
لملفAppWidgetProviderInfo
البيانات الوصفية. سيظل بإمكانك تحديد تنسيق التطبيق المصغّر الفعلي
للسمة initialLayout
واستخدام تنسيق التطبيق المصغّر الفعلي عند
إنشاء RemoteViews
في وقت التشغيل.
<appwidget-provider
previewLayout="@layout/widget_previe"
initialLayout="@layout/widget_view" />
عناصر القوائم المعقّدة
يقدّم المثال في القسم السابق عناصر قائمة مزيّفة، لأنّ عناصر القائمة هي عناصر TextView
. قد يكون من الصعوبة
تقديم عناصر مزيّفة إذا كانت العناصر تصاميم معقدة.
لنفترض أنّ لدينا عنصر قائمة محدّدًا في widget_list_item.xml
ويتألّف من
كائنَين من النوع TextView
:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView android:id="@id/title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/fake_title" />
<TextView android:id="@id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/fake_content" />
</LinearLayout>
لتقديم عناصر قائمة مزيّفة، يمكنك تضمين التنسيق عدة مرات، ولكن يؤدي ذلك إلى أن يكون كل عنصر في القائمة متطابقًا. لتقديم عناصر قائمة فريدة، اتّبِع الخطوات التالية:
أنشئ مجموعة من السمات لقيم النصوص:
<resources> <attr name="widgetTitle" format="string" /> <attr name="widgetContent" format="string" /> </resources>
استخدِم السمات التالية لضبط النص:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <TextView android:id="@id/title" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="?widgetTitle" /> <TextView android:id="@id/content" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="?widgetContent" /> </LinearLayout>
أنشئ العدد المطلوب من الأنماط لمعاينة المحتوى. أعِد تحديد القيم في كل نمط:
<resources> <style name="Theme.Widget.ListItem"> <item name="widgetTitle"></item> <item name="widgetContent"></item> </style> <style name="Theme.Widget.ListItem.Preview1"> <item name="widgetTitle">Fake Title 1</item> <item name="widgetContent">Fake content 1</item> </style> <style name="Theme.Widget.ListItem.Preview2"> <item name="widgetTitle">Fake title 2</item> <item name="widgetContent">Fake content 2</item> </style> </resources>
طبِّق الأنماط على العناصر المزيّفة في تنسيق المعاينة:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" ...> <include layout="@layout/widget_view" ... /> <include layout="@layout/widget_list_item" android:theme="@style/Theme.Widget.ListItem.Preview1" /> <include layout="@layout/widget_list_item" android:theme="@style/Theme.Widget.ListItem.Preview2" /> </LinearLayout>