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

בדף הזה מתוארים שיפורים לשינוי הגודל של הווידג'טים וגמישות רבה יותר. הושקו ב-Android 12 (רמת API 31). הוא גם מפרט קובעים את גודל הווידג'ט.

שימוש בממשקי API משופרים לגדלים ולפריסות של ווידג'טים

החל מ-Android 12 (רמת API 31), אפשר לשפר את הגודל ופריסות גמישות על ידי ביצוע הפעולות הבאות, כפי שמתואר הבאים:

  1. ציון מגבלות נוספות לגבי גודל הווידג'ט.

  2. שימוש בפריסות רספונסיביות או מדויקות פריסות.

בגרסאות קודמות של Android, אפשר לקבל טווחי גדלים של באמצעות OPTION_APPWIDGET_MIN_WIDTH OPTION_APPWIDGET_MIN_HEIGHT, OPTION_APPWIDGET_MAX_WIDTH, וגם OPTION_APPWIDGET_MAX_HEIGHT ואז להעריך את גודל הווידג'ט, אבל הלוגיקה הזו לא עובדת במצבים מסוימים. מומלץ להשתמש בווידג'טים שמטרגטים ל-Android מגרסה 12 ואילך לספק רספונסיבית או מדויקת פריסה.

ציון מגבלות נוספות של גודל הווידג'ט

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

בנוסף לדומיין minWidth הקיים, minHeight, minResizeWidth, וגם minResizeHeight השתמשו במאפיינים החדשים הבאים appwidget-provider:

  • targetCellWidth וגם targetCellHeight: להגדיר את גודל היעד של הווידג'ט במונחים של תאי רשת של מרכז האפליקציות. אם המיקום מוגדר, המאפיינים האלה משמשים במקום minWidth או minHeight.

  • maxResizeWidth וגם maxResizeHeight: להגדיר את הגודל המקסימלי שבו מרכז האפליקציות מאפשר למשתמש לשנות את גודל הווידג'ט.

קוד ה-XML הבא מראה איך להשתמש במאפייני הגודל.

<appwidget-provider
  ...
  android:targetCellWidth="3"
  android:targetCellHeight="2"
  android:maxResizeWidth="250dp"
  android:maxResizeHeight="110dp">
</appwidget-provider>

יצירת פריסות רספונסיביות

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

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

הקוד לדוגמה הבא מראה איך לספק רשימת פריסות.

Kotlin

override fun onUpdate(...) {
    val smallView = ...
    val tallView = ...
    val wideView = ...

    val viewMapping: Map<SizeF, RemoteViews> = mapOf(
            SizeF(150f, 100f) to smallView,
            SizeF(150f, 200f) to tallView,
            SizeF(215f, 100f) to wideView
    )
    val remoteViews = RemoteViews(viewMapping)

    appWidgetManager.updateAppWidget(id, remoteViews)
}

Java

@Override
public void onUpdate(...) {
    RemoteViews smallView = ...;
    RemoteViews tallView = ...;
    RemoteViews wideView = ...;

    Map<SizeF, RemoteViews> viewMapping = new ArrayMap<>();
    viewMapping.put(new SizeF(150f, 100f), smallView);
    viewMapping.put(new SizeF(150f, 200f), tallView);
    viewMapping.put(new SizeF(215f, 100f), wideView);
    RemoteViews remoteViews = new RemoteViews(viewMapping);

    appWidgetManager.updateAppWidget(id, remoteViews);
}

נניח שלווידג'ט יש את המאפיינים הבאים:

<appwidget-provider
    android:minResizeWidth="160dp"
    android:minResizeHeight="110dp"
    android:maxResizeWidth="250dp"
    android:maxResizeHeight="200dp">
</appwidget-provider>

המשמעות של קטע הקוד שלמעלה:

  • תמיכה ב-smallView החל מ-160dp (minResizeWidth) × 110dp (minResizeHeight) ל-160dp × 199dp (נקודת הסף הבאה – 1dp).
  • tallView תומך ב-160dp × 200dp עד 214dp (נקודת הסף הבאה - 1) × 200dp
  • wideView תומך ב-215dp × 110dp (minResizeHeight) עד 250dp (maxResizeWidth) × 200dp (maxResizeHeight).

הווידג'ט חייב לתמוך בטווח גדלים החל מ- minResizeWidth × minResizeHeight עד maxResizeWidth × maxResizeHeight. בטווח הזה, אפשר להחליט מהי נקודת הסף להחלפת פריסה.

דוגמה לפריסה רספונסיבית
איור 1. דוגמה לפריסה רספונסיבית.

ספק פריסות מדויקות

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

כדי להטמיע את הפתרון הזה, האפליקציה צריכה לבצע את השלבים הבאים:

  1. עומס יתר AppWidgetProvider.onAppWidgetOptionsChanged(), נקרא כשקבוצת הגדלים משתנה.

  2. קוראים לפונקציה AppWidgetManager.getAppWidgetOptions(), הפונקציה מחזירה את הערך Bundle שמכיל את המידות.

  3. נכנסים למפתח AppWidgetManager.OPTION_APPWIDGET_SIZES מ-Bundle.

הקוד לדוגמה הבא מראה איך לספק פריסות מדויקות.

Kotlin

override fun onAppWidgetOptionsChanged(
        context: Context,
        appWidgetManager: AppWidgetManager,
        id: Int,
        newOptions: Bundle?
) {
    super.onAppWidgetOptionsChanged(context, appWidgetManager, id, newOptions)
    // Get the new sizes.
    val sizes = newOptions?.getParcelableArrayList<SizeF>(
            AppWidgetManager.OPTION_APPWIDGET_SIZES
    )
    // Check that the list of sizes is provided by the launcher.
    if (sizes.isNullOrEmpty()) {
        return
    }
    // Map the sizes to the RemoteViews that you want.
    val remoteViews = RemoteViews(sizes.associateWith(::createRemoteViews))
    appWidgetManager.updateAppWidget(id, remoteViews)
}

// Create the RemoteViews for the given size.
private fun createRemoteViews(size: SizeF): RemoteViews { }

Java

@Override
public void onAppWidgetOptionsChanged(
    Context context, AppWidgetManager appWidgetManager, int appWidgetId, Bundle newOptions) {
    super.onAppWidgetOptionsChanged(context, appWidgetManager, appWidgetId, newOptions);
    // Get the new sizes.
    ArrayList<SizeF> sizes =
        newOptions.getParcelableArrayList(AppWidgetManager.OPTION_APPWIDGET_SIZES);
    // Check that the list of sizes is provided by the launcher.
    if (sizes == null || sizes.isEmpty()) {
      return;
    }
    // Map the sizes to the RemoteViews that you want.
    Map<SizeF, RemoteViews> viewMapping = new ArrayMap<>();
    for (SizeF size : sizes) {
        viewMapping.put(size, createRemoteViews(size));
    }
    RemoteViews remoteViews = new RemoteViews(viewMapping);
    appWidgetManager.updateAppWidget(id, remoteViews);
}

// Create the RemoteViews for the given size.
private RemoteViews createRemoteViews(SizeF size) { }

קביעת הגודל של הווידג'ט

לכל ווידג'ט צריך להגדיר targetCellWidth ו-targetCellHeight למכשירים מערכת Android בגרסה 12 ואילך, או minWidth ו-minHeight לכולם גרסאות של Android – מה שמציינים את כמות האחסון המינימלית שהיא צורכת כברירת מחדל. עם זאת, כשמשתמשים מוסיפים ווידג'ט למסך הבית, בדרך כלל גדול יותר מהרוחב והגובה המינימליים שציינתם.

מסכי הבית של Android מציעים למשתמשים רשת של מרחבים זמינים שבהם הם יכולים להציב ווידג'טים וסמלים. הרשת הזו יכולה להשתנות בהתאם למכשיר. לדוגמה, הרבה טלפונים ניידים מציעים רשת של 5x4 וטאבלטים יכולים להציע רשת גדולה יותר. כשהווידג'ט אחרי שמוסיפים אותו, הוא נמתח כך שיתפוס את המספר המינימלי של תאים, אנכית ואנכית, נדרשת כדי לעמוד במגבלות targetCellWidth ו-targetCellHeight במכשירים פועלים Android מגרסה 12 ואילך, או הגבלות על minWidth ו-minHeight שמופעלות מכשירים עם Android 11 (API ברמה 30) ומטה.

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

מספר התאים (רוחב x גובה) גודל זמין בפריסה לאורך (dp) גודל זמין בפריסה לרוחב (dp)
1x1 57x102dp 127x51dp
2x1 130x102dp 269x51dp
3x1 203x102dp 412x51dp
4x1 276x102dp 554x51dp
5x1 349x102dp 697x51dp
5x2 349x220dp 697x117dp
5x3 349x337dp 697x184dp
5x4 349x455dp 697x250dp
... ... ...
N x M (73n - 16) x (118m - 16) (142n - 15) x (66m - 15)

להשתמש בגדלים של התאים בפריסה לאורך כדי לציין את הערכים שמציינים המאפיינים minWidth, minResizeWidth ו-maxResizeWidth. באופן דומה, להשתמש בגדלים של התאים בפריסה לרוחב כדי לקבוע את הערכים שמספקים למאפיינים minHeight, minResizeHeight ו-maxResizeHeight.

הסיבה לכך היא שבדרך כלל רוחב התא קטן יותר בפריסה לאורך. יותר מאשר בפריסה לרוחב – ובאופן דומה, גובה התא הוא בדרך כלל קטן יותר בפריסה לרוחב מאשר לאורך.

לדוגמה, אם רוצים שניתן יהיה לשנות את הגודל של רוחב הווידג'ט עד לתא אחד Google Pixel 4, עליך להגדיר את minResizeWidth ל-56dp לכל היותר יש לוודא שהערך של המאפיין minResizeWidth קטן יותר מ-57dp - מכיוון שהתא הוא ברוחב של 57dp לפחות. באופן דומה, אם רוצים שניתן יהיה לשנות את גובה הווידג'ט בתא אחד באותו מכשיר, צריך להגדיר את minResizeHeight ל-50dp לכל היותר כדי לוודא ערך המאפיין minResizeHeight קטן מ- 51dp - כיוון שתא אחד הוא בגובה של לפחות 51dp בפריסה לרוחב.

ניתן לשנות את הגודל של כל ווידג'ט בטווחי הגדלים שבין minResizeWidth/minResizeHeight וגם maxResizeWidth/maxResizeHeight כלומר, הוא צריך להתאים את עצמו לכל טווח גודל שביניהם.

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

<appwidget-provider
    android:targetCellWidth="3"
    android:targetCellHeight="2"
    android:minWidth="180dp"
    android:minHeight="110dp">
</appwidget-provider>

המשמעות היא שגודל ברירת המחדל של הווידג'ט הוא 3x2 תאים, כפי שמצוין על ידי targetCellWidth ו-targetCellHeight – או 180×110dp, צוין על ידי minWidth ו-minHeight עבור מכשירים פועלים Android מגרסה 11 ומטה. במקרה השני, הגודל בתאים יכול בהתאם למכשיר.

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

<appwidget-provider
    android:minResizeWidth="180dp"
    android:minResizeHeight="110dp"
    android:maxResizeWidth="530dp"
    android:maxResizeHeight="450dp">
</appwidget-provider>

כפי שצוין במאפיינים הקודמים, רוחב הווידג'ט הוא ניתן לשנות את גודלו מ-180dp עד 530dp, וניתן לשנות את הגובה שלו מ-110dp עד 450dp. לאחר מכן ניתן לשנות את גודל הווידג'ט מתא 3x2 עד 5x2, כל עוד קיימים התנאים:

Kotlin

val smallView = RemoteViews(context.packageName, R.layout.widget_weather_forecast_small)
val mediumView = RemoteViews(context.packageName, R.layout.widget_weather_forecast_medium)
val largeView = RemoteViews(context.packageName, R.layout.widget_weather_forecast_large)

val viewMapping: Map<SizeF, RemoteViews> = mapOf(
        SizeF(180f, 110f) to smallView,
        SizeF(270f, 110f) to mediumView,
        SizeF(270f, 280f) to largeView
)

appWidgetManager.updateAppWidget(appWidgetId, RemoteViews(viewMapping))

Java

RemoteViews smallView = 
    new RemoteViews(context.getPackageName(), R.layout.widget_weather_forecast_small);
RemoteViews mediumView = 
    new RemoteViews(context.getPackageName(), R.layout.widget_weather_forecast_medium);
RemoteViews largeView = 
    new RemoteViews(context.getPackageName(), R.layout.widget_weather_forecast_large);

Map<SizeF, RemoteViews> viewMapping = new ArrayMap<>();
viewMapping.put(new SizeF(180f, 110f), smallView);
viewMapping.put(new SizeF(270f, 110f), mediumView);
viewMapping.put(new SizeF(270f, 280f), largeView);
RemoteViews remoteViews = new RemoteViews(viewMapping);

appWidgetManager.updateAppWidget(id, remoteViews);

נניח שהווידג'ט משתמש בפריסות הרספונסיביות שהוגדרו קודם קטעי קוד. כלומר, הפריסה שצוינה R.layout.widget_weather_forecast_small בשימוש החל מ- 180dp (minResizeWidth) x 110dp (minResizeHeight) עד 269x279dp (נקודות הסף הבאות – 1). באופן דומה, הגודל של R.layout.widget_weather_forecast_medium הוא 270x110dp עד 270x279dp. ו-R.layout.widget_weather_forecast_large משמש מ-270x280dp עד 530dp (maxResizeWidth) x 450dp (maxResizeHeight).

כשמשתמש משנה את גודל הווידג'ט, המראה שלו משתנה כדי להתאים אותו לכל גודל ב- של תאים, כמו בדוגמאות הבאות.

ווידג&#39;ט מזג אוויר לדוגמה בגודל הרשת הקטנה ביותר של 3x2. בממשק המשתמש מוצג
            שם המיקום (טוקיו), טמפרטורה (14°) וסמל שמציין
            מעונן חלקית.
איור 2. 3x2 R.layout.widget_weather_forecast_small.

דוגמה לווידג&#39;ט של מזג אוויר בגודל &#39;בינוני&#39; בגודל 4x2 גודל. שינוי גודל הווידג&#39;ט
            ככה הוא מבוסס על כל ממשק המשתמש מהגודל הקודם של הווידג&#39;ט,
            ומוסיף את התווית &#39;מעונן ברובו&#39; ותחזית של שינויי טמפרטורה
            16:00 עד 19:00.
איור 3. 4x2 R.layout.widget_weather_forecast_medium.

דוגמה לווידג&#39;ט של מזג אוויר בגודל &#39;בינוני&#39; בגודל 5x2 גודל. שינוי גודל הווידג&#39;ט
            כך שיתקבלו אותו ממשק משתמש כמו הגודל הקודם,
            נמתח על ידי תא אחד כדי לתפוס יותר מקום אופקי.
איור 4. 5x2 R.layout.widget_weather_forecast_medium.

דוגמה לווידג&#39;ט של מזג אוויר בגודל &#39;גדול&#39; בגודל 5x3 גודל. שינוי גודל הווידג&#39;ט
            ככה הוא מבוסס על כל ממשק המשתמש מהגדלים הקודמים של הווידג&#39;טים,
            ולהוסיף תצוגה בתוך הווידג&#39;ט שמכיל תחזית של מזג האוויר
            בימים שלישי ורביעי. סמלים שמציינים מזג אוויר שמשי או גשום
            ובטמפרטורות גבוהות ונמוכות בכל יום.
איור 5. 5x3 R.layout.widget_weather_forecast_large.

דוגמה לווידג&#39;ט של מזג אוויר בגודל &#39;גדול&#39; בגודל 5x4 גודל. שינוי גודל הווידג&#39;ט
            ככה הוא מבוסס על כל ממשק המשתמש מהגדלים הקודמים של הווידג&#39;טים,
            ומוסיפה את ימי חמישי ושישי (ואת הסמלים המתאימים
            שמציין את סוג מזג האוויר וגם טמפרטורה גבוהה ונמוכה
            בכל יום).
איור 6. 5x4 R.layout.widget_weather_forecast_large.