Membuat widget lanjutan

Halaman ini menjelaskan praktik yang direkomendasikan untuk membuat widget yang lebih canggih untuk pengalaman pengguna yang lebih baik.

Pengoptimalan untuk memperbarui konten widget

Memperbarui konten widget bisa menjadi mahal komputasinya. Untuk menghemat baterai pemakaian, mengoptimalkan jenis update, frekuensi, dan pengaturan waktu.

Jenis update widget

Ada tiga cara untuk memperbarui widget: pembaruan penuh, pembaruan sebagian, dan, dalam kasus widget koleksi, refresh data. Masing-masing memiliki biaya dan konsekuensi komputasional.

Berikut ini penjelasan untuk setiap jenis update dan cuplikan kode untuk setiap jenis update.

  • Info terbaru selengkapnya: panggil AppWidgetManager.updateAppWidget(int, android.widget.RemoteViews) untuk mengupdate widget sepenuhnya. Hal ini menggantikan metode yang disediakan RemoteViews dengan RemoteViews. Ini adalah update yang paling mahal secara komputasi.

    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);
    
  • Update sebagian: panggilan AppWidgetManager.partiallyUpdateAppWidget untuk memperbarui bagian-bagian dari widget. Tindakan ini akan menggabungkan RemoteViews baru dengan RemoteViews yang disediakan sebelumnya. Metode ini diabaikan jika widget tidak menerima setidaknya satu update lengkap melalui 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);
    
  • Pembaruan data koleksi: panggilan AppWidgetManager.notifyAppWidgetViewDataChanged untuk membatalkan validasi data tampilan koleksi di widget Anda. Hal ini memicu RemoteViewsFactory.onDataSetChanged Untuk sementara, data lama ditampilkan di widget. Anda dapat dengan aman melakukan tugas mahal secara sinkron dengan metode ini.

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

Anda bisa memanggil metode ini dari mana saja di aplikasi, selama aplikasi memiliki UID yang sama dengan Class AppWidgetProvider.

Menentukan frekuensi update widget

Widget diperbarui secara berkala bergantung pada nilai yang diberikan untuk updatePeriodMillis . Widget dapat diperbarui sebagai respons terhadap interaksi pengguna, siaran {i>update<i}, atau keduanya.

Perbarui secara berkala

Anda dapat mengontrol frekuensi pembaruan berkala dengan menentukan nilai untuk AppWidgetProviderInfo.updatePeriodMillis dalam XML appwidget-provider. Masing-masing update akan memicu metode AppWidgetProvider.onUpdate(), yang merupakan tempat Anda dapat menempatkan kode untuk memperbarui widget. Namun, pertimbangkan alternatif untuk penerima siaran terbaru yang dijelaskan dalam bagian berikut jika widget Anda harus memuat data secara asinkron atau mengambil lebih banyak dari 10 detik untuk memperbarui, karena setelah 10 detik, sistem menganggap BroadcastReceiver menjadi tidak responsif.

updatePeriodMillis tidak mendukung nilai kurang dari 30 menit. Namun, jika yang ingin Anda nonaktifkan pembaruan berkala, Anda bisa menetapkan 0.

Anda dapat mengizinkan pengguna menyesuaikan frekuensi pembaruan dalam konfigurasi. Sebagai mereka mungkin ingin ticker saham diperbarui setiap 15 menit atau hanya empat kali sehari. Dalam hal ini, setel updatePeriodMillis ke 0 dan gunakan WorkManager saja.

Pembaruan sebagai respons terhadap interaksi pengguna

Berikut adalah beberapa cara yang direkomendasikan untuk mengupdate widget berdasarkan interaksi pengguna:

  • Dari aktivitas aplikasi: panggil langsung AppWidgetManager.updateAppWidget sebagai respons terhadap interaksi pengguna, seperti sebagai ketukan pengguna.

  • Dari interaksi jarak jauh, seperti notifikasi atau widget aplikasi: membuat PendingIntent, lalu mengupdate widget dari fungsi yang dipanggil Activity, Broadcast, atau Service. Anda dapat memilih prioritas Anda sendiri. Sebagai misalnya, jika memilih Broadcast untuk PendingIntent, Anda dapat memilih siaran latar depan untuk memberikan Prioritas BroadcastReceiver.

Update sebagai respons terhadap acara siaran

Contoh acara siaran yang mengharuskan widget diperbarui adalah saat mengambil foto. Dalam hal ini, Anda ingin memperbarui widget ketika ada foto baru terdeteksi.

Anda dapat menjadwalkan tugas dengan JobScheduler dan menentukan siaran sebagai pemicu menggunakan ID JobInfo.Builder.addTriggerContentUri .

Anda juga dapat mendaftarkan BroadcastReceiver untuk siaran tersebut—misalnya, dengerin ACTION_LOCALE_CHANGED. Namun, karena ini menghabiskan resource perangkat, gunakan ini dengan hati-hati dan dengarkan hanya untuk siaran tertentu. Dengan diperkenalkannya siaran batasan di Android 7.0 (level API 24) dan Android 8.0 (level API 26), aplikasi tidak dapat mendaftar secara implisit {i>broadcast <i}dalam manifesnya, dengan pengecualian.

Pertimbangan saat mengupdate widget dari BroadcastReceiver

Jika widget diupdate dari BroadcastReceiver, termasuk AppWidgetProvider, perhatikan pertimbangan berikut terkait durasi dan prioritas pembaruan widget.

Durasi update

Biasanya, sistem mengizinkan penerima siaran, yang biasanya berjalan di utas utama, jalankan hingga 10 detik sebelum menganggapnya sebagai non-responsif dan memicu Application Not Merespons (ANR). Jika membutuhkan waktu lebih lama untuk mengupdate widget, pertimbangkan alternatif berikut:

  • Menjadwalkan tugas menggunakan WorkManager.

  • Beri penerima lebih banyak waktu dengan Metode goAsync. Hal ini memungkinkan penerima mengeksekusi selama 30 detik.

Lihat Pertimbangan keamanan dan untuk mengetahui tidak akurat atau tidak sesuai.

Prioritas update

Secara default, siaran—termasuk yang dibuat menggunakan AppWidgetProvider.onUpdate—jalankan sebagai proses latar belakang. Artinya resource sistem yang kelebihan beban dapat menyebabkan penundaan pemanggilan siaran penerima. Untuk memprioritaskan siaran, jadikan siaran tersebut sebagai proses latar depan.

Misalnya, tambahkan Intent.FLAG_RECEIVER_FOREGROUND ke Intent yang diteruskan ke PendingIntent.getBroadcast saat pengguna mengetuk bagian tertentu dari widget.

Membuat pratinjau akurat yang menyertakan item dinamis

Gambar 1: Pratinjau widget yang tidak menampilkan item daftar.

Bagian ini menjelaskan pendekatan yang direkomendasikan untuk menampilkan beberapa item dalam pratinjau widget untuk widget dengan koleksi tampilan—yaitu, widget yang menggunakan ListView, GridView, atau StackView.

Jika widget Anda menggunakan salah satu tampilan ini, buat pratinjau skalabel dengan langsung menyediakan widget menurunkan pengalaman saat pratinjau widget tidak menampilkan item. Hal ini terjadi karena data tampilan koleksi disetel secara dinamis saat runtime, dan terlihat mirip dengan gambar yang ditampilkan dalam gambar 1.

Untuk membuat pratinjau widget dengan tampilan koleksi ditampilkan dengan benar di widget sebaiknya Anda menyediakan file tata letak terpisah yang hanya ditujukan pratinjau. File tata letak terpisah ini mencakup tata letak widget yang sebenarnya dan tampilan koleksi placeholder dengan item palsu. Misalnya, Anda bisa meniru ListView dengan memberikan placeholder LinearLayout yang berisi beberapa daftar palsu item.

Untuk menggambarkan contoh ListView, mulai dengan file tata letak terpisah:

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

Tentukan file tata letak pratinjau saat memberikan atribut previewLayout metadata AppWidgetProviderInfo. Anda masih menentukan tata letak widget yang sebenarnya untuk atribut initialLayout dan gunakan tata letak widget yang sebenarnya saat membangun RemoteViews pada runtime.

<appwidget-provider
    previewLayout="@layout/widget_previe"
    initialLayout="@layout/widget_view" />

Item daftar kompleks

Contoh di bagian sebelumnya memberikan item daftar palsu, karena daftar item adalah objek TextView. Bisa lebih kompleks untuk menyediakan item palsu jika item tersebut memiliki tata letak yang kompleks.

Pertimbangkan item daftar yang ditentukan di widget_list_item.xml dan terdiri dari dua objek 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>

Untuk memberikan item daftar palsu, Anda dapat menyertakan tata letak beberapa kali, tetapi ini menyebabkan setiap item daftar menjadi identik. Untuk menyediakan item daftar yang unik, ikuti langkah-langkah berikut:

  1. Buat kumpulan atribut untuk nilai teks:

    <resources>
        <attr name="widgetTitle" format="string" />
        <attr name="widgetContent" format="string" />
    </resources>
    
  2. Gunakan atribut berikut untuk menetapkan teks:

    <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. Buat gaya sebanyak yang diperlukan untuk pratinjau. Definisikan ulang nilai-nilai di setiap gaya:

    <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. Terapkan gaya pada item palsu di tata letak pratinjau:

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