יצירת ווידג'ט מתקדם

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

אופטימיזציות לעדכון תוכן של ווידג'טים

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

סוגי העדכונים לווידג'טים

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

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

  • עדכון מלא: שיחה 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);
    

תוכלו להפעיל את השיטות האלה מכל מקום באפליקציה, כל עוד באפליקציה יש אותו UID כמו 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. עם זאת, מכיוון שהפעולה הזו צורכת משאבים במכשיר, חשוב להפעיל שיקול דעת ולהאזין רק לשידור הספציפי. עם ההשקה של broadcast מגבלות ב-Android 7.0 (רמת API 24) ו-Android 8.0 (רמת API 26), אפליקציות לא יכולות לבצע רישום משתמע משודרים במניפסטים שלהם, חריגים.

שיקולים לעדכון ווידג'ט מ- BroadcastReceiver

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

משך העדכון

בדרך כלל, המערכת מאפשרת למקלטי שידורים, שפועלים בדרך כלל ה-thread הראשי, ירוץ למשך עד 10 שניות לפני שהוא נחשב ללא תגובה הפעלת יישום לא שגיאת תגובה (ANR). אם התהליך נמשך זמן רב יותר, ומעדכנים את הווידג'ט, לשקול את החלופות הבאות:

  • לתזמן משימה באמצעות WorkManager.

  • נותנים למקבל התשלום עוד זמן באמצעות goAsync. כך המקלטים יכולים לפעול במשך 30 שניות.

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

עדיפות העדכון

כברירת מחדל, שידורים — כולל שידורים שבוצעו באמצעות AppWidgetProvider.onUpdate – להריץ כתהליכים ברקע. כלומר עומס יתר במשאבי מערכת עלול לגרום לעיכוב בהפעלת השידור הנמען. כדי לתת עדיפות לשידור, צריך להפוך אותו לתהליך שפועל בחזית.

לדוגמה, הוסף את Intent.FLAG_RECEIVER_FOREGROUND דגל ל-Intent שמועבר אל PendingIntent.getBroadcast כשהמשתמש שמקישים על חלק מסוים בווידג'ט.

יצירת תצוגות מקדימות מדויקות שכוללות פריטים דינמיים

איור 1: תצוגה מקדימה של ווידג'ט שלא מציגה פריטים ברשימה.

הקטע הזה מסביר את הגישה המומלצת להצגת מספר פריטים תצוגה מקדימה של ווידג'ט לווידג'ט עם אוסף תצוגה מפורטת – כלומר, ווידג'ט שמשתמש ListView, GridView או StackView.

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

כדי שתצוגות מקדימות של ווידג'טים עם תצוגות אוספים יוצגו כראוי בווידג'ט. מומלץ לתחזק קובץ פריסה נפרד שמיועד רק תצוגה מקדימה. קובץ הפריסה הנפרד הזה כולל את פריסת הווידג'ט עצמו תצוגת אוסף placeholder עם פריטים מזויפים. לדוגמה, אפשר לחקות ListView על ידי הוספת placeholder של 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>

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

  1. יוצרים קבוצת מאפיינים לערכי הטקסט:

    <resources>
        <attr name="widgetTitle" format="string" />
        <attr name="widgetContent" format="string" />
    </resources>
    
  2. יש להשתמש במאפיינים הבאים כדי להגדיר את הטקסט:

    <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>
    
  3. אפשר ליצור כמה סגנונות שרוצים לתצוגה המקדימה. הגדירו מחדש את הערכים ב- כל סגנון:

    <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>
    
  4. החלת הסגנונות על הפריטים המזויפים בפריסת התצוגה המקדימה:

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