בקטעים הבאים מוסבר איך ליצור ווידג'ט בסיסי לאפליקציה באמצעות Glance.
הצהרה על AppWidget במניפסט
אחרי שמסיימים את שלבי ההגדרה, מצהירים על AppWidget ועל המטא-נתונים שלו באפליקציה.
מרחיבים את המקלט
AppWidgetמ-GlanceAppWidgetReceiver:class MyAppWidgetReceiver : GlanceAppWidgetReceiver() { override val glanceAppWidget: GlanceAppWidget = TODO("Create GlanceAppWidget") }
רושמים את הספק של הווידג'ט של האפליקציה בקובץ
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 |
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 כגודל ברירת המחדל של הווידג'ט.
כברירת מחדל, גודל הווידג'ט הוא 2x2. אפשר לשנות את גודל הווידג'ט ל-2x1 או ל-4x3.
Android מגרסה 11 ומטה:
משתמשים במאפיינים minWidth ו-minHeight כדי לחשב את גודל ברירת המחדל של הווידג'ט.
רוחב ברירת המחדל = Math.ceil(80 / 30) = 3
גובה ברירת המחדל = Math.ceil(80 / 50) = 2
כברירת מחדל, גודל הווידג'ט הוא 3x2. אפשר לשנות את גודל הווידג'ט ל-2x1 או למסך מלא.
מאפיינים נוספים של הווידג'ט
בטבלה הבאה מתוארים המאפיינים של <appwidget-provider> שקשורים לתכונות אחרות, מלבד גודל הווידג'ט.
| מאפיינים ותיאור | |
|---|---|
updatePeriodMillis |
מגדירה את התדירות שבה מסגרת הווידג'ט מבקשת עדכון מ-GlanceAppWidgetReceiver באמצעות קריאה לשיטת הקריאה החוזרת (callback) onUpdate(). כדי לחסוך בסוללה, מומלץ לעדכן כמה שפחות, ולא יותר מפעם בשעה.
לפרטים נוספים, קראו את הקטע מתי לעדכן ווידג'טים במאמר "ניהול המצבים של Glance". |
initialLayout |
מפנה למשאב הפריסה שמגדיר את פריסת הטעינה של הווידג'ט לפני שמתבצע רינדור של הקומפוזיציות של ממשק המשתמש של Glance. אפשר להשתמש בפריסת הטעינה המוגדרת מראש שזמינה בספרייה: @layout/glance_default_loading_layout. |
configure |
מגדיר את פעילות ההגדרה שמופעלת כשהמשתמש מוסיף את הווידג'ט. איך מאפשרים למשתמשים להגדיר ווידג'טים של אפליקציות |
description |
מציין את התיאור שיוצג לווידג'ט בכלי לבחירת ווידג'טים. התכונה נוספה ב-Android 12. |
previewLayout (Android מגרסה 12) ו-previewImage (Android מגרסה 11 ומטה) |
|
autoAdvanceViewId |
מציין את מזהה התצוגה של תצוגת המשנה של הווידג'ט, שמועברת אוטומטית על ידי המארח של הווידג'ט. |
widgetCategory |
מצהירה אם הווידג'ט יכול להופיע במסך הבית (home_screen), במסך הנעילה (keyguard) או בשניהם. ב-Android מגרסה 5.0 ואילך, רק home_screen תקף. |
widgetFeatures |
מצהיר על התכונות שהווידג'ט תומך בהן. לדוגמה, אם הגדרת הווידג'ט היא אופציונלית, צריך לציין גם את configuration_optional וגם את reconfigurable. |
הגדרה של GlanceAppWidget
יוצרים מחלקה חדשה שמרחיבה את
GlanceAppWidgetומבטלת את ה-methodprovideGlance. זו השיטה שבה אפשר לטעון נתונים שנדרשים כדי לעבד את הווידג'ט: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") } } }
יוצרים מופע שלו ב-
glanceAppWidgetב-GlanceAppWidgetReceiver:class MyAppWidgetReceiver : GlanceAppWidgetReceiver() { // Let MyAppWidgetReceiver know which GlanceAppWidget to use override val glanceAppWidget: GlanceAppWidget = MyAppWidget() }
עכשיו הגדרתם AppWidget באמצעות Glance.
ניהול הזיכרון של תמונות בווידג'טים לפי קטגוריות גודל
כשבווידג'ט מוצגים קובצי ביטמאפ או מזהי URI של תמונות ב-content://, צריך לשמור על הזיכרון הכולל של התמונות ב-RemoteViews שעברו סריאליזציה במסגרת מגבלת המערכת (6 * width *
height בייטים, נאכפת באמצעות IllegalArgumentException ב-Android בגרסה 17 ואילך) על ידי ביצוע הפעולות הבאות:
- פענוח של מפות סיביות (bitmap) משותפות לפני
provideContent: אם הווידג'ט משתמש ב-SizeMode.Responsiveאו ב-SizeMode.Exact, Glance מפעיל את פונקציית הלמבדהprovideContentפעם אחת לכל קטגוריה שלDpSize. פענוח שלBitmapבתוךprovideContentמקצה מופע נפרד שלBitmapלכל קטגוריה, ומכפיל את השימוש בזיכרון ב-RemoteViews.mBitmapCache. מפענחים את מפות הסיביות שעברו דגימה מופחתת (Bitmap.Config.RGB_565לתמונות אטומות) פעם אחת בתוךprovideGlanceלפניprovideContent, כדי שכל הקטגוריות ישתפו הפניה אחת שלBitmap. משתמשים בייבוא התואם של
ImageProvider: Jetpack Glance מספק שתי פונקציות נפרדות שלImageProviderבשתי חבילות:- משתמשים ב-
import androidx.glance.ImageProviderכשמעביריםImageProvider(bitmap: Bitmap)בזיכרון או פריט גרפי שניתן להזזהImageProvider(resId: Int). - משתמשים ב-
import androidx.glance.appwidget.ImageProviderכשמעבירים URIImageProvider(uri: Uri). כשמציגים קבצים שנשמרו במטמון דרך URI שלFileProvider, צריך להעניק את ההרשאהIntent.FLAG_GRANT_READ_URI_PERMISSIONלכל חבילת מרכז אפליקציות שלIntent.CATEGORY_HOMEשמותקנת, כדי שהמארח יוכל לעבד את התמונה.
- משתמשים ב-
שימוש בכיתה GlanceAppWidgetReceiver כדי לטפל בשידורי ווידג'טים
הווידג'ט של הקואורדינטות ב-GlanceAppWidgetReceiver משדר עדכונים לגבי המצב של הפלטפורמה על ידי הרחבת ה-AppWidgetProvider הבסיסי. הוא מקבל אירועים מהפלטפורמה כשהווידג'ט מתעדכן, נמחק, מופעל או מושבת, ומתרגם אותם לבקשות של מחזור החיים של Compose.
הצהרה על ווידג'ט במניפסט
מגדירים את מחלקת המשנה של המחלקה GlanceAppWidgetReceiver כמקלט שידור בקובץ AndroidManifest.xml:
<receiver android:name="MyReceiver"
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 צריך להצביע על משאב המטא-נתונים של AppWidgetProviderInfo בפורמט XML (@xml/my_app_widget_info).
הטמעת המחלקה GlanceAppWidgetReceiver
ב-Glance, מרחיבים את GlanceAppWidgetReceiver במקום את AppWidgetProvider ישירות. כדי להטמיע את זה, צריך לקשר את המקלט למכונה של GlanceAppWidget. הקריאות החוזרות (callbacks) העיקריות שזמינות ב-GlanceAppWidgetReceiver פועלות באופן הבא:
-
onUpdate(): מוחלף באופן אוטומטי על ידי Glance כדי לבצע עדכונים בקומפוזיציה. אם אתם משנים באופן ידני אתonUpdate, אתם צריכים להפעיל אתsuper.onUpdateכדי לאפשר ל-Glance להפעיל בהצלחה את ה-threads של הקומפוזיציה. -
onAppWidgetOptionsChanged(): נקראת כשהווידג'ט ממוקם בפעם הראשונה או כשמשנים את הגודל שלו. האפשרויות של Glance reads מקבצות פריטים מאחורי הקלעים, כך שהפריסה מתאימה את עצמה בצורה חלקה על סמך המאפיינים של זמן הריצה. -
onDeleted(Context, IntArray): מופעל בכל פעם שהמשתמש מוחק מופע ספציפי של ווידג'ט. -
onEnabled(Context): מופעל כשהמופע הראשון של הווידג'ט נוצר בהצלחה. מעולה להעברות גלובליות. -
onDisabled(Context): מופעלת כשמוסר המופע הפעיל האחרון של הספק. -
onReceive(Context, Intent): מיירט כל שידור של הפלטפורמה לפני שיטות קריאה חוזרת ספציפיות. חשוב לוודא שכל לוגיקה של מקלט בהתאמה אישית שאתם כותבים קוראת ל-super.onReceive(context, intent), ולעולם לא קוראת ל-goAsyncבעצמה, כי Glance מנתב את העבודה באופן אסינכרוני.
קבלת כוונות שידור של ווידג'טים
מאחורי הקלעים, GlanceAppWidgetReceiver מסנן ומטפל בכוונות השידור הבאות של הווידג'ט של הפלטפורמה הבסיסית:
ACTION_APPWIDGET_UPDATEACTION_APPWIDGET_DELETEDACTION_APPWIDGET_ENABLEDACTION_APPWIDGET_DISABLEDACTION_APPWIDGET_OPTIONS_CHANGED
יצירת ממשק משתמש
בקטע הקוד הבא אפשר לראות איך יוצרים את ממשק המשתמש:
/* 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מוגדר באמצעות lambda. הסדר חשוב.
אתם יכולים לשנות את ערכי היישור או להחיל ערכי שינוי שונים (כמו מרווח פנימי) כדי לשנות את המיקום והגודל של הרכיבים. במאמרי העזרה מופיעה רשימה מלאה של רכיבים, פרמטרים ומשתנים זמינים לכל מחלקה.
הטמעת פינות מעוגלות
ב-Android 12 נוספו פרמטרים של המערכת להתאמה אישית של רדיוסי הפינות של הווידג'טים של האפליקציה באופן דינמי:
-
system_app_widget_background_radius: מציין את רדיוס הפינה של מאגר הרקע של הווידג'ט (אף פעם לא גדול מ-28dp). - רדיוס פנימי: כדי למנוע חיתוך של התוכן, מחשבים רדיוס פרופורציונלי לתוכן הפנימי על סמך קווי המתאר של הרקע של המערכת:
systemRadiusValue - widgetPadding
ב-Glance, אפשר להחיל מאפייני גודל של רדיוס פינה באופן דינמי בקומפוזיציה באמצעות GlanceModifier.cornerRadius(android.R.dimen.system_app_widget_background_radius).
כדי לשמור על תאימות לדורות קודמים במכשירים עם Android 11 (רמת API 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>