יצירת ווידג'ט פשוט

אנסה לכתוב
‫Jetpack Compose היא ערכת הכלים המומלצת לבניית ממשק משתמש ל-Android. איך יוצרים ווידג'טים באמצעות ממשקי API בסגנון Compose

ווידג'טים של אפליקציות הם תצוגות מוקטנות של אפליקציות שאפשר להטמיע באפליקציות אחרות – כמו מסך הבית – ולקבל עדכונים תקופתיים. התצוגות האלה נקראות ווידג'טים בממשק המשתמש, ואפשר לפרסם אותן באמצעות ספק ווידג'טים לאפליקציות (או ספק ווידג'טים). רכיב אפליקציה שמכיל ווידג'טים אחרים נקרא מארח ווידג'טים של אפליקציה (או מארח ווידג'טים). איור 1 מציג דוגמה לווידג'ט של מוזיקה:

דוגמה לווידג'ט מוזיקה
איור 1. דוגמה לווידג'ט מוזיקה.

במאמר הזה מוסבר איך לפרסם ווידג'ט באמצעות ספק ווידג'טים. פרטים על יצירת AppWidgetHost משלכם לאירוח ווידג'טים של אפליקציות מופיעים במאמר יצירת מארח ווידג'טים.

מידע על עיצוב הווידג'ט זמין במאמר סקירה כללית על ווידג'טים של אפליקציות.

רכיבי הווידג'ט

כדי ליצור ווידג'ט, צריך את הרכיבים הבסיסיים הבאים:

אובייקט AppWidgetProviderInfo
מתאר את המטא-נתונים של הווידג'ט, כמו הפריסה, תדירות העדכון והסיווג AppWidgetProvider של הווידג'ט. AppWidgetProviderInfo מוגדר ב-XML, כפי שמתואר במסמך הזה.
כיתה AppWidgetProvider
מגדיר את השיטות הבסיסיות שמאפשרות לכם ליצור ממשק עם הווידג'ט באופן פרוגרמטי. באמצעותה, תקבלו שידורים כשהווידג'ט יעודכן, יופעל, יושבת או יימחק. מצהירים על AppWidgetProvider במניפסט ואז מטמיעים אותו, כמו שמתואר במסמך הזה.
פריסת התצוגה
הגדרת הפריסה הראשונית של הווידג'ט. הפריסה מוגדרת ב-XML, כפי שמתואר במסמך הזה.

באיור 2 מוצג איך הרכיבים האלה משתלבים בתהליך הכולל של עיבוד הווידג'ט של האפליקציה.

תהליך העיבוד של ווידג'ט האפליקציה
איור 2. תרשים זרימה של עיבוד ווידג'ט של אפליקציה.

אם הווידג'ט שלכם צריך הגדרת משתמש, צריך להטמיע את פעילות ההגדרה של הווידג'ט של האפליקציה. הפעילות הזו מאפשרת למשתמשים לשנות את הגדרות הווידג'ט – לדוגמה, אזור הזמן של ווידג'ט השעון.

מומלץ גם להשתמש בשיפורים הבאים: פריסות גמישות של ווידג'טים, שיפורים שונים, ווידג'טים מתקדמים, ווידג'טים של אוספים ויצירת מארח לווידג'טים.

הצהרה על קובץ ה-XML של AppWidgetProviderInfo

אובייקט AppWidgetProviderInfo מגדיר את המאפיינים החיוניים של הווידג'ט. מגדירים את האובייקט AppWidgetProviderInfo בקובץ משאבי XML באמצעות רכיב <appwidget-provider> יחיד, ושומרים אותו בתיקייה res/xml/ של הפרויקט.

כך זה נראה בדוגמה הבאה:

<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/example_loading_appwidget"
    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 ההגדרה הזו קובעת את התדירות שבה מסגרת הווידג'טים מבקשת עדכון מ-AppWidgetProvider על ידי קריאה לשיטת הקריאה החוזרת onUpdate(). העדכון בפועל לא יתבצע בדיוק בזמן שצוין בערך הזה, ומומלץ לעדכן בתדירות נמוכה ככל האפשר – לא יותר מפעם בשעה – כדי לחסוך בסוללה. רשימת השיקולים המלאה לבחירת תקופת עדכון מתאימה מופיעה במאמר אופטימיזציות לעדכון תוכן הווידג'ט.
initialLayout מצביע על משאב הפריסה שמגדיר את פריסת הווידג'ט.
configure המדיניות הזו מגדירה את הפעילות שמופעלת כשהמשתמש מוסיף את הווידג'ט, ומאפשרת לו להגדיר את מאפייני הווידג'ט. מידע נוסף זמין במאמר איך מאפשרים למשתמשים להגדיר ווידג'טים. החל מ-Android 12, האפליקציה יכולה לדלג על ההגדרה הראשונית. פרטים נוספים זמינים במאמר שימוש בהגדרת ברירת המחדל של הווידג'ט.
description מציינים את התיאור של בוחר הווידג'טים שיוצג עבור הווידג'ט. הוצג ב-Android 12.
previewLayout (Android 12) ו-previewImage (Android 11 ומטה)
  • החל מ-Android 12, המאפיין previewLayout מציין תצוגה מקדימה שניתן לשנות את הגודל שלה, ואתם מספקים אותה כפריסת XML שמוגדרת לגודל ברירת המחדל של הווידג'ט. מומלץ שקובץ ה-XML של הפריסה שצוין במאפיין הזה יהיה זהה לקובץ ה-XML של הפריסה של הווידג'ט בפועל, עם ערכי ברירת מחדל ריאליים.
  • ב-Android 11 או בגרסאות קודמות, המאפיין previewImage מציין תצוגה מקדימה של הווידג'ט אחרי ההגדרה, שהמשתמש רואה כשהוא בוחר את הווידג'ט של האפליקציה. אם לא מספקים סמל, המשתמש יראה במקומו את סמל מרכז האפליקציות של האפליקציה. השדה הזה תואם למאפיין android:previewImage ברכיב <receiver> בקובץ AndroidManifest.xml.
הערה: מומלץ לציין את המאפיינים previewImage ו-previewLayout כדי שהאפליקציה תוכל לחזור לשימוש ב-previewImage אם המכשיר של המשתמש לא תומך ב-previewLayout. מידע נוסף מופיע בקטע תאימות לאחור עם תצוגות מקדימות של ווידג'טים שניתנים לשינוי גודל.
autoAdvanceViewId מציין את מזהה התצוגה של תצוגת המשנה של הווידג'ט, שמועברת אוטומטית על ידי המארח של הווידג'ט.
widgetCategory הצהרה אם הווידג'ט יכול להיות מוצג במסך הבית (home_screen), במסך הנעילה (keyguard) או בשניהם. ב-Android מגרסה 5.0 ואילך, רק home_screen תקף.
widgetFeatures התג הזה מציין את התכונות שהווידג'ט תומך בהן. לדוגמה, אם רוצים שהווידג'ט ישתמש בהגדרת ברירת המחדל שלו כשמשתמש מוסיף אותו, צריך לציין את הדגלים configuration_optional ו-reconfigurable. כך לא צריך להפעיל את פעילות ההגדרה אחרי שהמשתמש מוסיף את הווידג'ט. המשתמש עדיין יכול להגדיר מחדש את הווידג'ט בהמשך.

שימוש במחלקה AppWidgetProvider כדי לטפל בשידורים של הווידג'ט

המחלקות AppWidgetProvider מטפלות בשידורי הווידג'טים ומעדכנות את הווידג'ט בתגובה לאירועים במחזור החיים של הווידג'ט. בקטעים הבאים מוסבר איך להצהיר על AppWidgetProvider בקובץ המניפסט ואז להטמיע אותו.

הצהרה על ווידג'ט במניפסט

קודם כול, צריך להצהיר על המחלקה AppWidgetProvider בקובץ AndroidManifest.xml של האפליקציה, כמו בדוגמה הבאה:

<receiver android:name="ExampleAppWidgetProvider"
                 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/example_appwidget_info" />
</receiver>

אלמנט <receiver> מחייב את המאפיין android:name, שמציין את AppWidgetProvider שמשמש את הווידג'ט. אסור לייצא את הרכיב, אלא אם יש צורך בתהליך נפרד כדי לשדר ל-AppWidgetProvider, מה שבדרך כלל לא קורה.

הרכיב <intent-filter> חייב לכלול רכיב <action> עם המאפיין android:name. במאפיין הזה מציינים שהשידור AppWidgetProvider מתקבל בACTION_APPWIDGET_UPDATE. זו השידור היחיד שחובה להצהיר עליו באופן מפורש. ‫AppWidgetManager שולח אוטומטית את כל שידורי הווידג'טים האחרים אל AppWidgetProvider לפי הצורך.

רכיב <meta-data> מציין את משאב AppWidgetProviderInfo וכולל את מאפייני החובה הבאים:

  • android:name: מציין את שם המטא-נתונים. משתמשים ב-android.appwidget.provider כדי לזהות את הנתונים כמתאר AppWidgetProviderInfo.
  • android:resource: מציין את AppWidgetProviderInfo מיקום המשאב.

הטמעה של המחלקה AppWidgetProvider

המחלקות AppWidgetProvider class extends BroadcastReceiver הן מחלקות נוחות לטיפול בשידורי ווידג'טים. הוא מקבל רק את שידורי האירועים שרלוונטיים לווידג'ט, למשל כשהווידג'ט מתעדכן, נמחק, מופעל או מושבת. כשאירועי השידור האלה מתרחשים, מופעלות הפונקציות הבאות של AppWidgetProvider:

onUpdate()
This is called to update the widget at intervals defined by the updatePeriodMillis attribute in the AppWidgetProviderInfo. מידע נוסף זמין בטבלה שמתארת מאפיינים נוספים של ווידג'טים שבדף הזה.
השיטה הזו מופעלת גם כשהמשתמש מוסיף את הווידג'ט, ולכן היא מבצעת את ההגדרה החיונית, כמו הגדרת handlers של אירועים לאובייקטים של View או הפעלת משימות לטעינת נתונים להצגה בווידג'ט. עם זאת, אם מצהירים על פעילות הגדרה בלי הדגל configuration_optional, השיטה הזו לא נקראת כשהמשתמש מוסיף את הווידג'ט, אבל היא כן נקראת לעדכונים הבאים. הפעילות של ההגדרה אחראית לבצע את העדכון הראשון כשההגדרה מסתיימת. מידע נוסף זמין במאמר הפעלת האפשרות למשתמשים להגדיר ווידג'טים של אפליקציות.
הקריאה החוזרת החשובה ביותר היא onUpdate(). מידע נוסף זמין בקטע טיפול באירועים באמצעות המחלקה onUpdate() בדף הזה.
onAppWidgetOptionsChanged()

הפונקציה הזו מופעלת כשהווידג'ט ממוקם בפעם הראשונה ובכל פעם שמשנים את הגודל שלו. אפשר להשתמש בקריאה החוזרת הזו כדי להציג או להסתיר תוכן בהתאם לטווחים של גודל הווידג'ט. כדי לקבל את טווחי הגודל – ומתחילת Android 12, את רשימת הגדלים האפשריים שמופע של ווידג'ט יכול להיות בהם – צריך לקרוא ל-getAppWidgetOptions(), שמחזירה Bundle שכוללת את הפרטים הבאים:

  • OPTION_APPWIDGET_MIN_WIDTH: מכיל את הגבול התחתון של הרוחב, ביחידות dp, של מופע של ווידג'ט.
  • OPTION_APPWIDGET_MIN_HEIGHT: מכיל את הגבול התחתון של הגובה, ביחידות dp, של מופע של ווידג'ט.
  • OPTION_APPWIDGET_MAX_WIDTH: מכיל את הגבול העליון של הרוחב, ביחידות dp, של מופע של ווידג'ט.
  • OPTION_APPWIDGET_MAX_HEIGHT: מכיל את הגבול העליון של הגובה, ביחידות dp, של מופע של ווידג'ט.
  • OPTION_APPWIDGET_SIZES: מכיל את רשימת הגדלים האפשריים (List<SizeF>), ביחידות dp, שמופע של ווידג'ט יכול לתפוס. הוצג ב-Android 12.
onDeleted(Context, int[])

הפונקציה הזו מופעלת בכל פעם שווידג'ט נמחק ממארח הווידג'טים.

onEnabled(Context)

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

onDisabled(Context)

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

onReceive(Context, Intent)

הקריאה הזו מתבצעת לכל שידור ולפני כל אחת מהשיטות הקודמות של callback. בדרך כלל לא צריך להטמיע את השיטה הזו, כי הטמעת ברירת המחדל AppWidgetProviderמסננת את כל השידורים והקריאות של הווידג'טים לשיטות הקודמות, בהתאם לצורך.

צריך להצהיר על ההטמעה של המחלקה AppWidgetProvider כ-broadcast receiver באמצעות הרכיב <receiver> ב-AndroidManifest. מידע נוסף מופיע בקטע הצהרה על ווידג'ט במניפסט בדף הזה.

טיפול באירועים באמצעות המחלקה onUpdate()‎

הקריאה החוזרת הכי חשובה היא AppWidgetProvider, כי היא מופעלת כשכל ווידג'ט מתווסף למארח, אלא אם משתמשים בפעילות הגדרה בלי הדגל configuration_optional.onUpdate() אם הווידג'ט מקבל אירועי אינטראקציה כלשהם של המשתמש, צריך לרשום את הפונקציות לטיפול באירועים בקריאה החוזרת הזו. אם הווידג'ט לא יוצר קבצים או מסדי נתונים זמניים, או לא מבצע פעולות אחרות שדורשות ניקוי, יכול להיות ש-onUpdate() היא שיטת הקריאה החוזרת היחידה שתצטרכו להגדיר.

לדוגמה, אם רוצים להוסיף לווידג'ט לחצן שפותח פעילות כשמקישים עליו, אפשר להשתמש בהטמעה הבאה של AppWidgetProvider:

Kotlin

class ExampleAppWidgetProvider : AppWidgetProvider() {

    override fun onUpdate(
            context: Context,
            appWidgetManager: AppWidgetManager,
            appWidgetIds: IntArray
    ) {
        // Perform this loop procedure for each widget that belongs to this
        // provider.
        appWidgetIds.forEach { appWidgetId ->
            // Create an Intent to launch ExampleActivity.
            val pendingIntent: PendingIntent = PendingIntent.getActivity(
                    /* context = */ context,
                    /* requestCode = */  0,
                    /* intent = */ Intent(context, ExampleActivity::class.java),
                    /* flags = */ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
            )

            // Get the layout for the widget and attach an onClick listener to
            // the button.
            val views: RemoteViews = RemoteViews(
                    context.packageName,
                    R.layout.appwidget_provider_layout
            ).apply {
                setOnClickPendingIntent(R.id.button, pendingIntent)
            }

            // Tell the AppWidgetManager to perform an update on the current
            // widget.
            appWidgetManager.updateAppWidget(appWidgetId, views)
        }
    }
}

Java

public class ExampleAppWidgetProvider extends AppWidgetProvider {

    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        // Perform this loop procedure for each widget that belongs to this
        // provider.
        for (int i=0; i < appWidgetIds.length; i++) {
            int appWidgetId = appWidgetIds[i];
            // Create an Intent to launch ExampleActivity
            Intent intent = new Intent(context, ExampleActivity.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(
                /* context = */ context,
                /* requestCode = */ 0,
                /* intent = */ intent,
                /* flags = */ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
            );

            // Get the layout for the widget and attach an onClick listener to
            // the button.
            RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.example_appwidget_layout);
            views.setOnClickPendingIntent(R.id.button, pendingIntent);

            // Tell the AppWidgetManager to perform an update on the current app
            // widget.
            appWidgetManager.updateAppWidget(appWidgetId, views);
        }
    }
}

הקוד AppWidgetProvider מגדיר רק את השיטה onUpdate(), ומשתמש בה כדי ליצור PendingIntent שמפעיל Activity ומצרף אותו ללחצן של הווידג'ט באמצעות setOnClickPendingIntent(int, PendingIntent). הוא כולל לולאה שחוזרת על עצמה לכל רשומה ב-appWidgetIds, שהוא מערך של מזהים שמזהים כל ווידג'ט שנוצר על ידי הספק הזה. אם המשתמש יוצר יותר ממופע אחד של הווידג'ט, כולם מתעדכנים בו-זמנית. עם זאת, רק updatePeriodMillisלוח זמנים אחד מנוהל עבור כל המופעים של הווידג'ט. לדוגמה, אם לוח הזמנים של העדכון מוגדר להיות כל שעתיים, ומוסיפים מופע שני של הווידג'ט שעה אחרי הראשון, שניהם מתעדכנים בתקופה שהוגדרה על ידי הראשון, ותקופת העדכון השנייה מתעלמת. שניהם מתעדכנים כל שעתיים, ולא כל שעה.

פרטים נוספים זמינים בכיתה לדוגמה ExampleAppWidgetProvider.java.

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

AppWidgetProvider היא מחלקת נוחות. אם רוצים לקבל את השידורים של הווידג'ט ישירות, אפשר להטמיע BroadcastReceiver משלכם או לבטל את ברירת המחדל של הקריאה החוזרת onReceive(Context,Intent). הכוונה שחשוב להתייחס אליה היא:

יצירת פריסת הווידג'ט

צריך להגדיר פריסה ראשונית לווידג'ט ב-XML ולשמור אותה בספרייה res/layout/ של הפרויקט. פרטים נוספים זמינים בהנחיות לעיצוב.

אם אתם מכירים את הפריסות, קל ליצור את פריסת הווידג'טים. עם זאת, חשוב לדעת שפריסות הווידג'טים מבוססות על RemoteViews, שלא תומך בכל סוג של פריסה או תצוגת ווידג'ט. אי אפשר להשתמש בתצוגות בהתאמה אישית או בתת-מחלקות של התצוגות שנתמכות על ידי RemoteViews.

RemoteViews תומך גם ב-ViewStub, שהוא View בלתי נראה בגודל אפס שאפשר להשתמש בו כדי להגדיל משאבי פריסה בזמן ריצה.

תמיכה בהתנהגות עם שמירת מצב

ב-Android 12 נוספה תמיכה בהתנהגות עם שמירת מצב באמצעות הרכיבים הקיימים הבאים:

הווידג'ט עדיין חסר מצב. האפליקציה צריכה לשמור את המצב שלה ולהירשם לאירועים של שינוי מצב.

דוגמה לווידג&#39;ט של רשימת קניות שמציג התנהגות עם שמירת מצב
איור 3. דוגמה להתנהגות עם שמירת מצב.

בדוגמת הקוד הבאה אפשר לראות איך מטמיעים את הרכיבים האלה.

Kotlin

// Check the view.
remoteView.setCompoundButtonChecked(R.id.my_checkbox, true)

// Check a radio group.
remoteView.setRadioGroupChecked(R.id.my_radio_group, R.id.radio_button_2)

// Listen for check changes. The intent has an extra with the key
// EXTRA_CHECKED that specifies the current checked state of the view.
remoteView.setOnCheckedChangeResponse(
        R.id.my_checkbox,
        RemoteViews.RemoteResponse.fromPendingIntent(onCheckedChangePendingIntent)
)

Java

// Check the view.
remoteView.setCompoundButtonChecked(R.id.my_checkbox, true);

// Check a radio group.
remoteView.setRadioGroupChecked(R.id.my_radio_group, R.id.radio_button_2);

// Listen for check changes. The intent has an extra with the key
// EXTRA_CHECKED that specifies the current checked state of the view.
remoteView.setOnCheckedChangeResponse(
    R.id.my_checkbox,
    RemoteViews.RemoteResponse.fromPendingIntent(onCheckedChangePendingIntent));

צריך לספק שני פריסות: אחת שמטרגטת מכשירים עם Android 12 ומעלה בתיקייה res/layout-v31, והשנייה שמטרגטת מכשירים עם Android 11 ומטה בתיקייה res/layout שמוגדרת כברירת מחדל.

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

ב-Android 12 נוספו הפרמטרים הבאים של המערכת כדי להגדיר את רדיוס הפינות המעוגלות של הווידג'ט:

  • system_app_widget_background_radius: רדיוס הפינות של הרקע של הווידג'ט, שלא יכול להיות גדול מ-28 dp.

  • הרדיוס הפנימי, שאפשר לחשב אותו מהרדיוס החיצוני ומהריווח הפנימי. אפשר לעיין בקטע הקוד הבא:

    /**
     * Applies corner radius for views that are visually positioned [widgetPadding]dp inside of the
     * widget background.
     */
    @Composable
    fun GlanceModifier.appWidgetInnerCornerRadius(widgetPadding: Dp): GlanceModifier {
    
        if (Build.VERSION.SDK_INT < 31) {
            return this
        }
    
        val resources = LocalContext.current.resources
        // get dimension in float (without rounding).
        val px = resources.getDimension(android.R.dimen.system_app_widget_background_radius)
        val widgetBackgroundRadiusDpValue = px / resources.displayMetrics.density
        if (widgetBackgroundRadiusDpValue < widgetPadding.value) {
            return this
        }
        return this.cornerRadius(Dp(widgetBackgroundRadiusDpValue - widgetPadding.value))
    }

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

בווידג'טים שגוזרים את התוכן שלהם לצורות לא מלבניות, צריך להשתמש ב-@android:id/background כמזהה התצוגה של תצוגת הרקע שבה android:clipToOutline מוגדר כ-true.

שיקולים חשובים לגבי פינות מעוגלות

  • יצרני מכשירים ומשגרים של צד שלישי יכולים לשנות את הפרמטר system_app_widget_background_radius כך שיהיה קטן מ-28 dp.
  • אם הווידג'ט לא משתמש ב-@android:id/background או לא מגדיר רקע שגוזר את התוכן שלו על סמך המתאר – עם android:clipToOutline שהוגדר ל-true – מרכז האפליקציות יזהה את הרקע באופן אוטומטי ויגזור את הווידג'ט באמצעות מלבן עם פינות מעוגלות שהוגדר לרדיוס המערכת.

  • צורות לא מלבניות צריכות להיות כלולות בתוך מאגר השינוי של המלבן המעוגל, כדי שלא ייחתכו.

  • החל מ-Android 16, ערך המערכת AOSP עבור system_app_widget_background_radius הוא 24dp. יצרני מכשירים ואפליקציות להפעלת אפליקציות עשויים לחתוך את הווידג'ט לsystem_app_widget_background_radius.

  • התוכן הפנימי של הווידג'ט צריך לכלול מספיק ריווח פנימי כדי לתמוך בsystem_app_widget_background_radius ערכי רדיוס של עד 28dp, וכך למנוע חיתוך של התוכן על ידי הפינות המעוגלות.

כדי שהווידג'ט יהיה תואם לגרסאות קודמות של Android, מומלץ להגדיר מאפיינים מותאמים אישית ולהשתמש בערכת נושא מותאמת אישית כדי לבטל את ההגדרות שלהם ב-Android 12, כמו שמוצג בקובצי ה-XML לדוגמה הבאים:

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

/layout/my_widget_layout.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  ...
  android:background="@drawable/my_widget_background" />