יצירת ווידג'ט לאפליקציה באמצעות התכונה 'בקצרה'

מניפסט, מטא-נתונים

בקטעים הבאים מוסבר איך ליצור ווידג'ט בסיסי לאפליקציה באמצעות 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.

דוגמה

כדי להמחיש איך המאפיינים בטבלה הקודמת משפיעים על גודל הווידג'ט, נניח שיש את המפרטים הבאים:

  • הרוחב של תא ברשת הוא 30dp והגובה שלו הוא 50dp.
  • מפורט כאן מפרט המאפיינים:
<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 על ידי קריאה לשיטת הקריאה החוזרת onUpdate(). כדי לחסוך בסוללה, מומלץ לעדכן את המיקום בתדירות נמוכה ככל האפשר – לא יותר מפעם בשעה. פרטים נוספים זמינים בקטע מתי לעדכן ווידג'טים במאמר בנושא ניהול מצב של תצוגה מהירה.
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(): מופעל כשהווידג'ט ממוקם או משנה את הגודל שלו בפעם הראשונה. האפשרויות של קריאה מהירה מאגדות פריטים מתחת לפני השטח, כך שהפריסה מותאמת בצורה חלקה על סמך המידות בזמן הריצה.
  • onDeleted(Context, IntArray): מופעל בכל פעם שמשתמש מוחק מופע ספציפי של ווידג'ט.
  • onEnabled(Context): מופעל כשהמופע הראשון של הווידג'ט נוצר בהצלחה. מתאים מאוד להפעלת העברות גלובליות.
  • onDisabled(Context): מופעלת כשמסירים את המופע הפעיל האחרון של הספק.
  • onReceive(Context, Intent): חוסם כל שידור בפלטפורמה לפני שיטות קריאה חוזרת ספציפיות. חשוב לוודא שכל לוגיקה של מקלט מותאם אישית שאתם כותבים קוראת ל-super.onReceive(context, intent) ואף פעם לא קוראת ל-goAsync בעצמכם, כי Glance מעביר עבודה באופן אוטומטי אסינכרוני.

קבלת כוונות שידור של ווידג'טים

מתחת לפני השטח, GlanceAppWidgetReceiver מסנן ומטפל בכוונות השידור הבאות של הווידג'טים הבסיסיים של הפלטפורמה:

Create UI

בקטע הקוד הבא אפשר לראות איך יוצרים את ממשק המשתמש:

/* 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. הסדר חשוב.
    • הפריט הראשון ב-Column הוא רכיב Text עם 12.dp של ריווח פנימי.
    • הפריט השני הוא Row, שבו הפריטים מוצבים אופקית אחד אחרי השני, עם שני Buttons שממורכזים אופקית (horizontalAlignment). התצוגה הסופית תלויה במקום שזמין. דוגמה למראה של כרטיס כזה:
destination_widget
תרשים 1. ממשק משתמש לדוגמה.

אתם יכולים לשנות את ערכי היישור או להחיל ערכי שינוי שונים (כמו ריווח פנימי) כדי לשנות את המיקום והגודל של הרכיבים. רשימה מלאה של הרכיבים, הפרמטרים והמשנים הזמינים לכל מחלקה מופיעה במסמכי העזר.

הטמעה של פינות מעוגלות

ב-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>