Widget Aplikasi adalah tampilan aplikasi mini yang dapat disematkan dalam aplikasi lain (seperti Layar utama) dan menerima update berkala. Tampilan ini disebut sebagai Widget dalam antarmuka pengguna, dan Anda dapat memublikasikannya dengan penyedia Widget Aplikasi. Komponen aplikasi yang dapat menampung Widget Aplikasi lain disebut host Widget Aplikasi. Screenshot di bawah ini menunjukkan Widget Aplikasi Musik.

Dokumen ini menjelaskan cara memublikasikan Widget Aplikasi menggunakan penyedia
Widget Aplikasi. Untuk pembahasan tentang cara membuat AppWidgetHost
untuk menghosting widget aplikasi, lihat
Host Widget Aplikasi.
Catatan: Untuk informasi tentang cara mendesain widget aplikasi, baca Ringkasan Widget Aplikasi.
Dasar-Dasar
Untuk membuat Widget Aplikasi, Anda memerlukan hal-hal berikut:
- Objek
AppWidgetProviderInfo
- Menjelaskan metadata untuk Widget Aplikasi, seperti tata letak Widget Aplikasi, frekuensi update, dan class AppWidgetProvider. Hal ini harus ditentukan dalam XML.
- Penerapan class
AppWidgetProvider
- Menentukan metode dasar yang memungkinkan Anda berinteraksi secara terprogram dengan Widget Aplikasi, berdasarkan peristiwa siaran. Melalui aplikasi ini, Anda akan menerima siaran saat Widget Aplikasi diupdate, diaktifkan, dinonaktifkan, dan dihapus.
- Tata letak tampilan
- Menentukan tata letak awal Widget Aplikasi, yang ditentukan dalam XML.
Sebagai tambahan, Anda dapat menerapkan Aktivitas konfigurasi Widget Aplikasi. Ini
merupakan
Activity
opsional yang diluncurkan saat pengguna menambahkan Widget Aplikasi Anda
dan memungkinkan pengguna
memodifikasi setelan Widget Aplikasi pada waktu pembuatan.
Bagian berikut menjelaskan cara menyiapkan setiap komponen ini.
Mendeklarasikan Widget Aplikasi di Manifes
Pertama, deklarasikan class AppWidgetProvider
dalam
file AndroidManifest.xml
aplikasi. Contoh:
<receiver android:name="ExampleAppWidgetProvider" > <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>
Elemen <receiver>
memerlukan atribut
android:name
yang menentukan AppWidgetProvider
yang digunakan
oleh Widget Aplikasi.
Elemen <intent-filter>
harus berisi elemen
<action>
dengan atribut android:name
. Atribut ini menentukan
bahwa AppWidgetProvider
menerima siaran ACTION_APPWIDGET_UPDATE
.
Siaran ini merupakan satu-satunya yang harus Anda deklarasikan secara eksplisit. AppWidgetManager
secara otomatis mengirimkan semua siaran Widget Aplikasi ke AppWidgetProvider sesuai
kebutuhan.
Elemen <meta-data>
menentukan resource
AppWidgetProviderInfo
dan memerlukan
atribut berikut:
android:name
- Menentukan nama metadata. Gunakanandroid.appwidget.provider
untuk mengidentifikasi data sebagai deskriptorAppWidgetProviderInfo
.android:resource
- Menentukan lokasi resourceAppWidgetProviderInfo
.
Menambahkan Metadata AppWidgetProviderInfo
AppWidgetProviderInfo
menentukan kualitas penting
Widget Aplikasi, seperti dimensi tata letak minimum,
resource tata letak awal,
seberapa sering update dilakukan untuk Widget Aplikasi, dan (secara opsional) peluncuran Aktivitas konfigurasi
pada waktu pembuatan.
Tentukan objek AppWidgetProviderInfo dalam resource XML menggunakan satu
elemen <appwidget-provider>
dan simpan ke folder
res/xml/
project.
Contoh:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" android:minWidth="40dp" android:minHeight="40dp" android:updatePeriodMillis="86400000" android:previewImage="@drawable/preview" android:initialLayout="@layout/example_appwidget" android:configure="com.example.android.ExampleAppWidgetConfigure" android:resizeMode="horizontal|vertical" android:widgetCategory="home_screen"> </appwidget-provider>
Berikut adalah ringkasan atribut <appwidget-provider>
:
- Nilai untuk atribut
minWidth
danminHeight
menentukan jumlah ruang minimum yang digunakan Widget Aplikasi secara default. Layar utama default menentukan posisi Widget Aplikasi dalam jendela berdasarkan petak sel yang memiliki tinggi dan lebar yang ditentukan. Jika nilai untuk lebar atau tinggi minimum Widget Aplikasi tidak sesuai dengan dimensi sel, dimensi Widget Aplikasi akan dibulatkan ke atas ke ukuran sel terdekat.Lihat Panduan Desain Widget Aplikasi untuk mengetahui informasi selengkapnya tentang pengukuran Widget Aplikasi Anda.
Catatan: Untuk menjadikan widget aplikasi portabel di seluruh perangkat, ukuran minimum widget aplikasi Anda tidak boleh melebihi sel 4 x 4.
- Atribut
minResizeWidth
danminResizeHeight
menentukan ukuran minimum absolut Widget Aplikasi. Nilai ini harus menentukan ukuran yang akan menjadikan Widget Aplikasi tidak terbaca atau tidak dapat dipakai jika berada di bawahnya. Penggunaan atribut ini memungkinkan pengguna mengubah ukuran widget ke ukuran yang lebih kecil dari ukuran default widget yang ditentukan oleh atributminWidth
danminHeight
. Diperkenalkan di Android 3.1.Lihat Panduan Desain Widget Aplikasi untuk mengetahui informasi selengkapnya tentang pengukuran Widget Aplikasi Anda.
- Atribut
updatePeriodMillis
menentukan seberapa sering framework Widget Aplikasi harus meminta update dariAppWidgetProvider
dengan memanggil metode callbackonUpdate()
. Update yang sebenarnya belum tentu terjadi tepat waktu dengan nilai ini dan sebaiknya Anda mengupdate sesering mungkin, tetapi-tidak lebih dari satu kali dalam satu jam untuk menghemat baterai. Anda juga dapat mengizinkan pengguna menyesuaikan frekuensi di konfigurasi—beberapa orang mungkin ingin mengupdate simbol saham setiap 15 menit, atau mungkin hanya empat kali dalam satu hari.Catatan: Jika perangkat beralih ke mode tidur saat waktunya update (seperti yang ditetapkan oleh
updatePeriodMillis
), perangkat akan diaktifkan untuk menjalankan update. Jika Anda tidak mengupdate lebih dari satu kali dalam satu jam, hal ini mungkin tidak akan menyebabkan masalah yang signifikan pada masa pakai baterai Anda. Namun, jika Anda perlu mengupdate lebih sering dan/atau Anda tidak perlu mengupdate saat perangkat dalam mode tidur, Anda dapat menjalankan update berdasarkan alarm yang tidak akan mengaktifkan perangkat. Untuk melakukannya, setel alarm dengan intent yang akan diterima AppWidgetProvider Anda menggunakanAlarmManager
. Setel jenis alarm keELAPSED_REALTIME
atauRTC
, yang hanya akan membunyikan alarm saat perangkat aktif. Kemudian, setelupdatePeriodMillis
ke nol ("0"
). - Atribut
initialLayout
mengarah ke resource tata letak yang menentukan tata letak Widget Aplikasi. - Atribut
configure
menentukanActivity
yang akan diluncurkan saat pengguna menambahkan Widget Aplikasi, untuk mengonfigurasi properti Widget Aplikasi. Langkah ini bersifat opsional (baca Membuat Aktivitas Konfigurasi Widget Aplikasi di bawah). - Atribut
previewImage
menentukan pratinjau tampilan widget aplikasi setelah dikonfigurasi, yang akan dilihat oleh pengguna saat memilih widget aplikasi tersebut. Jika tidak disediakan, pengguna akan melihat ikon peluncur aplikasi Anda. Kolom ini sesuai dengan atributandroid:previewImage
dalam elemen<receiver>
di fileAndroidManifest.xml
. Untuk pembahasan lebih lanjut tentang penggunaanpreviewImage
, lihat Menyetel Gambar Pratinjau. Diperkenalkan di Android 3.0. - Atribut
autoAdvanceViewId
menentukan ID tampilan sub-tampilan widget aplikasi yang harus dimajukan secara otomatis oleh host widget. Diperkenalkan di Android 3.0. - Atribut
resizeMode
menentukan aturan yang menjadikan ukuran widget dapat diubah. Anda dapat menggunakan atribut ini untuk membuat widget layar utama dapat diubah ukurannya—secara horizontal, vertikal, atau pada kedua sumbu. Pengguna dapat menyentuh lama widget untuk menampilkan tuas pengubah ukuran, lalu menarik tuas horizontal dan/atau vertikal untuk mengubah ukuran pada petak tata letak. Nilai atributresizeMode
mencakup "horizontal", "vertikal", dan "tidak ada". Untuk mendeklarasikan widget dapat diubah ukurannya secara horizontal dan vertikal, berikan nilai "horizontal|vertical". Diperkenalkan di Android 3.1. - Atribut
minResizeHeight
menentukan tinggi minimum (dalam dps) yang menjadi acuan untuk mengubah ukuran widget. Kolom ini tidak berpengaruh jika lebih besar dariminHeight
atau jika pengubahan ukuran secara vertikal tidak diaktifkan (lihatresizeMode
). Diperkenalkan di Android 4.0. - Atribut
minResizeWidth
menentukan lebar minimum (dalam dps) yang menjadi acuan untuk mengubah ukuran widget. Kolom ini tidak berpengaruh jika lebih besar dariminWidth
atau jika pengubahan ukuran secara horizontal tidak diaktifkan (lihatresizeMode
). Diperkenalkan di Android 4.0. - Atribut
widgetCategory
mendeklarasikan apakah Widget Aplikasi Anda dapat ditampilkan di layar utama (home_screen
), layar kunci (keyguard
), atau keduanya. Hanya versi Android yang lebih rendah dari 5.0 yang mendukung widget layar kunci. Untuk Android 5.0 dan yang lebih tinggi, hanyahome_screen
yang valid.
Lihat class AppWidgetProviderInfo
untuk mengetahui
informasi selengkapnya tentang
atribut yang diterima oleh elemen <appwidget-provider>
.
Membuat Tata Letak Widget Aplikasi
Anda harus menentukan tata letak awal untuk Widget Aplikasi Anda dalam XML dan menyimpannya dalam
direktori
projek res/layout/
. Anda dapat mendesain Widget Aplikasi menggunakan
objek View yang ditampilkan
di bawah, tetapi sebelum memulai mendesain Widget Aplikasi, baca dan
pahami
Panduan
Desain
Widget Aplikasi.
Pembuatan tata letak Widget Aplikasi cukup sederhana jika Anda
memahami Tata Letak.
Namun, perlu diketahui bahwa tata letak Widget Aplikasi didasarkan pada RemoteViews
,
yang tidak mendukung setiap jenis tata letak atau widget tampilan.
Objek RemoteViews (dan Widget Aplikasi) dapat mendukung class tata letak berikut ini:
Class widget berikut juga didukung:
AnalogClock
Button
Chronometer
ImageButton
ImageView
ProgressBar
TextView
ViewFlipper
ListView
GridView
StackView
AdapterViewFlipper
Turunan dari semua class tersebut tidak didukung.
RemoteViews juga mendukung ViewStub
, yang merupakan Tampilan tidak terlihat berukuran nol yang dapat digunakan
untuk memperluas resource tata letak sesuai keinginan pada waktu proses.
Menambahkan margin ke Widget Aplikasi
Biasanya, widget tidak boleh diperluas ke tepi layar dan tidak boleh secara visual digantikan dengan widget lain, sehingga Anda perlu menambahkan margin pada semua sisi di sekitar bingkai widget.
Mulai versi Android 4.0, widget aplikasi akan secara otomatis mendapat padding di antara bingkai widget dan kotak pembatas widget aplikasi untuk memberikan perataan yang lebih baik dengan ikon dan widget lain pada layar utama pengguna. Untuk memanfaatkan perilaku yang sangat direkomendasikan ini, setel targetSdkVersion aplikasi ke 14 atau lebih besar.
Anda dapat dengan mudah menulis tata letak yang memiliki margin kustom yang diterapkan untuk versi sebelumnya, dan tidak memiliki margin tambahan untuk Android 4.0 atau lebih besar.
- Setel aplikasi
targetSdkVersion
Anda ke 14 atau lebih besar. - Buat tata letak seperti yang ditunjukkan di bawah ini, yang mengacu pada resource dimensi untuk marginnya.
<FrameLayout android:layout_width="match_parent" android:layout_height="match_parent" android:padding="@dimen/widget_margin"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" android:background="@drawable/my_widget_background"> … </LinearLayout> </FrameLayout>
- Buat dua resource dimensi, satu di
res/values/
untuk memberikan margin kustom sebelum Android 4.0, dan satu dires/values-v14/
untuk tidak memberikan padding tambahan bagi widget Android 4.0:res/values/dimens.xml:
<dimen name="widget_margin">8dp</dimen>
res/values-v14/dimens.xml:
<dimen name="widget_margin">0dp</dimen>
Opsi lainnya adalah dengan hanya membuat margin tambahan ke aset latar belakang 9-patch secara default, dan menyediakan 9-patch yang berbeda tanpa margin untuk API level 14 atau yang lebih baru.
Menggunakan Class AppWidgetProvider
Class AppWidgetProvider
memperluas
BroadcastReceiver sebagai class praktis
untuk menangani siaran Widget Aplikasi. AppWidgetProvider hanya menerima
siaran peristiwa yang
relevan dengan Widget Aplikasi, seperti saat Widget Aplikasi diupdate, dihapus,
diaktifkan, dan dinonaktifkan.
Saat peristiwa siaran ini terjadi, AppWidgetProvider menerima panggilan
metode berikut:
-
onUpdate()
- Metode ini dipanggil untuk mengupdate Widget Aplikasi pada interval yang ditentukan oleh atribut
updatePeriodMillis
dalam AppWidgetProviderInfo (lihat Menambahkan Metadata AppWidgetProviderInfo di atas). Metode ini juga dipanggil saat pengguna menambahkan Widget Aplikasi agar dapat melakukan penyiapan penting, seperti menentukan pengendali peristiwa untuk Tampilan dan membuatService
sementara, jika perlu. Namun jika Anda telah mendeklarasikan Aktivitas konfigurasi, metode ini tidak akan dipanggil saat pengguna menambahkan Widget Aplikasi, tetapi akan dipanggil untuk update berikutnya. Aktivitas konfigurasi bertanggung jawab untuk melakukan update pertama saat konfigurasi selesai. (Lihat Membuat Aktivitas Konfigurasi Widget Aplikasi di bawah.) -
onAppWidgetOptionsChanged()
-
Callback ini dipanggil saat widget pertama kali ditempatkan dan setiap kali ukuran widget diubah. Anda dapat menggunakan callback ini untuk menampilkan atau menyembunyikan konten berdasarkan rentang ukuran widget. Anda mendapatkan rentang ukuran dengan memanggil
getAppWidgetOptions()
, yang akan menampilkanBundle
yang menyertakan hal-hal berikut:
OPTION_APPWIDGET_MIN_WIDTH
—Berisi batas bawah pada lebar saat ini, dalam satuan dp, dari instance widget.OPTION_APPWIDGET_MIN_HEIGHT
—Berisi batas bawah pada tinggi saat ini, dalam satuan dp, dari instance widget.OPTION_APPWIDGET_MAX_WIDTH
—Berisi batas atas pada lebar saat ini, dalam satuan dp, dari instance widget.OPTION_APPWIDGET_MAX_HEIGHT
—Berisi batas atas pada tinggi saat ini, dalam satuan dp, dari instance widget.
onDeleted(Context, int[])
- Callback ini dipanggil setiap kali Widget Aplikasi dihapus dari host Widget Aplikasi.
onEnabled(Context)
- Callback ini dipanggil saat instance Widget Aplikasi dibuat untuk pertama kalinya. Misalnya, jika pengguna menambahkan dua instance Widget Aplikasi, callback ini hanya dipanggil pada pembuatan pertama. Jika Anda perlu membuka database baru atau menjalankan penyiapan lain yang hanya perlu dilakukan sekali untuk semua instance Widget Aplikasi, ini merupakan tempat yang tepat untuk melakukannya.
onDisabled(Context)
- Callback ini dipanggil saat instance terakhir Widget Aplikasi Anda dihapus dari
host Widget Aplikasi.
Pada tahap ini, Anda harus menghapus semua pekerjaan yang dilakukan di
onEnabled(Context)
, seperti menghapus database sementara. onReceive(Context, Intent)
- Callback ini dipanggil untuk setiap siaran dan sebelum masing-masing metode callback di atas. Biasanya, Anda tidak perlu menerapkan metode ini karena penerapan AppWidgetProvider default akan memfilter semua siaran Widget Aplikasi dan memanggil metode di atas yang sesuai.
Anda harus mendeklarasikan penerapan class AppWidgetProvider sebagai
penerima siaran
menggunakan elemen <receiver>
dalam AndroidManifest (lihat
Mendeklarasikan Widget Aplikasi di Manifes di atas).
Callback AppWidgetProvider yang terpenting adalah
onUpdate()
karena dipanggil saat
masing-masing Widget Aplikasi ditambahkan ke host (kecuali jika Anda menggunakan Aktivitas konfigurasi). Jika
Widget Aplikasi Anda menerima peristiwa interaksi pengguna, Anda perlu mendaftarkan
pengendali peristiwa dalam callback ini. Jika Widget Aplikasi tidak membuat
file atau database sementara, atau menjalankan pekerjaan lain yang perlu dihapus,
onUpdate()
mungkin merupakan satu-satunya metode callback
yang perlu ditentukan. Misalnya, jika Anda ingin Widget Aplikasi memiliki tombol
yang meluncurkan Aktivitas saat diklik, Anda dapat menggunakan penerapan
AppWidgetProvider berikut:
Kotlin
class ExampleAppWidgetProvider : AppWidgetProvider() { override fun onUpdate( context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray ) { // Perform this loop procedure for each App Widget that belongs to this provider appWidgetIds.forEach { appWidgetId -> // Create an Intent to launch ExampleActivity val pendingIntent: PendingIntent = Intent(context, ExampleActivity::class.java) .let { intent -> PendingIntent.getActivity(context, 0, intent, 0) } // Get the layout for the App Widget and attach an on-click 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 app widget appWidgetManager.updateAppWidget(appWidgetId, views) } } }
Java
public class ExampleAppWidgetProvider extends AppWidgetProvider { public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { final int N = appWidgetIds.length; // Perform this loop procedure for each App Widget that belongs to this provider for (int i=0; i<N; i++) { int appWidgetId = appWidgetIds[i]; // Create an Intent to launch ExampleActivity Intent intent = new Intent(context, ExampleActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, 0); // Get the layout for the App Widget and attach an on-click listener // to the button RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout); views.setOnClickPendingIntent(R.id.button, pendingIntent); // Tell the AppWidgetManager to perform an update on the current app widget appWidgetManager.updateAppWidget(appWidgetId, views); } } }
AppWidgetProvider ini hanya menentukan metode
onUpdate()
untuk
menentukan PendingIntent
yang meluncurkan Activity
dan melampirkannya pada tombol Widget Aplikasi dengan setOnClickPendingIntent(int, PendingIntent)
. Perlu diketahui
bahwa hal tersebut mencakup loop yang melakukan iterasi melalui setiap entri dalam
appWidgetIds
, yang merupakan array ID yang mengidentifikasi setiap Widget
Aplikasi yang dibuat oleh penyedia ini. Dengan begitu, jika pengguna membuat lebih dari satu
instance Widget Aplikasi, semua instance akan diupdate secara bersamaan. Namun,
hanya satu jadwal updatePeriodMillis
yang akan dikelola untuk semua
instance Widget Aplikasi. Misalnya, jika jadwal update ditentukan
menjadi setiap dua jam sekali, dan instance kedua Widget Aplikasi ditambahkan satu jam
setelah yang pertama, keduanya akan diupdate pada periode yang ditentukan oleh
update pertama, dan periode update kedua akan diabaikan (keduanya akan diupdate
setiap dua jam, bukan setiap jam).
Catatan: Karena AppWidgetProvider
merupakan ekstensi dari BroadcastReceiver
, proses Anda tidak dijamin tetap
berjalan setelah metode callback kembali (lihat BroadcastReceiver
untuk informasi tentang siklus hidup
siaran). Jika proses penyiapan Widget Aplikasi membutuhkan beberapa detik (mungkin
saat menjalankan permintaan web) dan Anda perlu melanjutkan proses,
pertimbangkan untuk memulai Service
dalam
metode
onUpdate()
. Dari dalam Layanan, Anda dapat menjalankan update sendiri
pada Widget Aplikasi tanpa perlu khawatir AppWidgetProvider akan tertutup
karena error Aplikasi
Tidak Merespons (ANR). Lihat
Contoh AppWidgetProvider dari Wiktionary untuk melihat contoh Widget Aplikasi yang menjalankan Service
.
Lihat juga contoh class ExampleAppWidgetProvider.java.
Menerima Intent siaran Widget Aplikasi
AppWidgetProvider
hanyalah class praktis. Jika
ingin
menerima siaran Widget Aplikasi secara langsung, Anda dapat menerapkan
BroadcastReceiver
sendiri atau mengganti callback
onReceive(Context, Intent)
.
Berikut adalah intent yang perlu Anda perhatikan:
ACTION_APPWIDGET_UPDATE
ACTION_APPWIDGET_DELETED
ACTION_APPWIDGET_ENABLED
ACTION_APPWIDGET_DISABLED
ACTION_APPWIDGET_OPTIONS_CHANGED
Memasang Pin pada Widget Aplikasi
Pada perangkat yang menjalankan Android 8.0 (API level 26) dan yang lebih tinggi, peluncur yang memungkinkan Anda membuat pintasan yang dipasangi pin juga memungkinkan Anda memasang pin pada widget aplikasi ke peluncur. Seperti pintasan yang dipasangi pin, widget yang dipasangi pin memberi pengguna akses ke tugas tertentu di aplikasi Anda.
Di aplikasi, Anda dapat mengajukan permintaan pada sistem untuk memasang pin pada widget ke peluncur yang didukung dengan melakukan langkah-langkah berikut:
- Buat widget di file manifes aplikasi Anda seperti yang ditunjukkan dalam cuplikan
berikut:
<manifest> ... <application> ... <receiver android:name="MyAppWidgetProvider"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_UPDATE" /> </intent-filter> <meta-data android:name="android.appwidget.provider" android:resource="@xml/my_appwidget_info" /> </receiver> </application> </manifest>
- Panggil
metode
requestPinAppWidget()
, seperti yang ditunjukkan dalam cuplikan kode berikut:Kotlin
val appWidgetManager: AppWidgetManager = context.getSystemService(AppWidgetManager::class.java) val myProvider = ComponentName(context, MyAppWidgetProvider::class.java) val successCallback: PendingIntent? = if (appWidgetManager.isRequestPinAppWidgetSupported) { // Create the PendingIntent object only if your app needs to be notified // that the user allowed the widget to be pinned. Note that, if the pinning // operation fails, your app isn't notified. Intent(...).let { intent -> // Configure the intent so that your app's broadcast receiver gets // the callback successfully. This callback receives the ID of the // newly-pinned widget (EXTRA_APPWIDGET_ID). PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT) } } else { null } successCallback?.also { pendingIntent -> appWidgetManager.requestPinAppWidget(myProvider, null, pendingIntent) }
Java
AppWidgetManager appWidgetManager = context.getSystemService(AppWidgetManager.class); ComponentName myProvider = new ComponentName(context, MyAppWidgetProvider.class); if (appWidgetManager.isRequestPinAppWidgetSupported()) { // Create the PendingIntent object only if your app needs to be notified // that the user allowed the widget to be pinned. Note that, if the pinning // operation fails, your app isn't notified. Intent pinnedWidgetCallbackIntent = new Intent( ... ); // Configure the intent so that your app's broadcast receiver gets // the callback successfully. This callback receives the ID of the // newly-pinned widget (EXTRA_APPWIDGET_ID). PendingIntent successCallback = PendingIntent.getBroadcast(context, 0, pinnedWidgetCallbackIntent, PendingIntent.FLAG_UPDATE_CURRENT); appWidgetManager.requestPinAppWidget(myProvider, null, successCallback); }
Catatan: Jika aplikasi Anda tidak perlu diberi tahu tentang apakah
sistem berhasil memasang pin pada widget ke peluncur yang didukung, Anda dapat
meneruskan null
sebagai argumen ketiga ke
requestPinAppWidget()
.
Memuat Aktivitas Konfigurasi Widget Aplikasi
Jika Anda ingin pengguna mengonfigurasi setelan saat mereka menambahkan
Widget Aplikasi baru,
Anda dapat membuat Aktivitas konfigurasi Widget Aplikasi. Activity
ini
akan diluncurkan secara otomatis oleh host Widget Aplikasi dan memungkinkan pengguna
mengonfigurasi
setelan yang tersedia untuk Widget Aplikasi pada waktu pembuatan, seperti
warna, ukuran,
periode update, atau setelan fungsi lainnya.
Aktivitas konfigurasi seharusnya dideklarasikan sebagai Aktivitas normal di
file manifes Android.
Namun, Aktivitas tersebut akan diluncurkan oleh host Widget Aplikasi dengan tindakan ACTION_APPWIDGET_CONFIGURE
,
sehingga perlu menerima Intent ini. Contoh:
<activity android:name=".ExampleAppWidgetConfigure"> <intent-filter> <action android:name="android.appwidget.action.APPWIDGET_CONFIGURE"/> </intent-filter> </activity>
Selain itu, Aktivitas harus dideklarasikan di file XML AppWidgetProviderInfo
dengan atribut
android:configure
(lihat Menambahkan
Metadata AppWidgetProviderInfo di atas). Misalnya, Aktivitas
konfigurasi
dapat dideklarasikan seperti berikut:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" ... android:configure="com.example.android.ExampleAppWidgetConfigure" ... > </appwidget-provider>
Perhatikan bahwa Aktivitas dideklarasikan dengan namespace yang sepenuhnya memenuhi syarat, karena akan direferensikan dari luar cakupan paket Anda.
Hanya ini yang Anda perlukan untuk memulai Aktivitas konfigurasi. Sekarang yang Anda perlukan adalah Aktivitas sebenarnya. Namun, ada dua hal penting yang perlu diingat saat Anda menerapkan Aktivitas:
- Host Widget Aplikasi memanggil Aktivitas konfigurasi dan
Aktivitas konfigurasi harus selalu
menampilkan hasil. Hasil akan berisi ID Widget Aplikasi
yang diteruskan oleh Intent yang meluncurkan Aktivitas (yang disimpan dalam tambahan Intent
sebagai
EXTRA_APPWIDGET_ID
). - Metode
onUpdate()
tidak akan dipanggil saat Widget Aplikasi dibuat (sistem tidak akan mengirim siaran ACTION_APPWIDGET_UPDATE saat Aktivitas konfigurasi diluncurkan). Aktivitas konfigurasi bertanggung jawab untuk meminta update dari AppWidgetManager saat Widget Aplikasi pertama kali dibuat. Namun,onUpdate()
akan dipanggil untuk update berikutnya—hanya dilewatkan pada update pertama.
Lihat cuplikan kode di bagian berikut untuk contoh cara menampilkan hasil konfigurasi dan mengupdate Widget Aplikasi.
Mengupdate Widget Aplikasi dari Aktivitas konfigurasi
Saat Widget Aplikasi menggunakan Aktivitas konfigurasi, tanggung jawab
Aktivitas
adalah mengupdate Widget Aplikasi saat konfigurasi selesai.
Anda dapat melakukannya dengan meminta update secara langsung dari
AppWidgetManager
.
Berikut adalah ringkasan prosedur untuk mengupdate Widget Aplikasi dengan benar dan menutup Aktivitas konfigurasi:
- Pertama, dapatkan ID Widget Aplikasi dari Intent yang meluncurkan Aktivitas:
Kotlin
appWidgetId = intent?.extras?.getInt( AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID ) ?: AppWidgetManager.INVALID_APPWIDGET_ID
Java
Intent intent = getIntent(); Bundle extras = intent.getExtras(); if (extras != null) { appWidgetId = extras.getInt( AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); }
- Jalankan konfigurasi Widget Aplikasi.
- Setelah konfigurasi selesai, dapatkan instance
AppWidgetManager dengan memanggil
getInstance(Context)
:Kotlin
val appWidgetManager: AppWidgetManager = AppWidgetManager.getInstance(context)
Java
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
- Update Widget Aplikasi menggunakan tata letak
RemoteViews
dengan memanggilupdateAppWidget(int, RemoteViews)
:Kotlin
RemoteViews(context.packageName, R.layout.example_appwidget).also { views-> appWidgetManager.updateAppWidget(appWidgetId, views) }
Java
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.example_appwidget); appWidgetManager.updateAppWidget(appWidgetId, views);
- Terakhir, buat Intent hasil, lalu setel dengan hasil Aktivitas, dan
selesaikan Aktivitas:
Kotlin
val resultValue = Intent().apply { putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) } setResult(Activity.RESULT_OK, resultValue) finish()
Java
Intent resultValue = new Intent(); resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId); setResult(RESULT_OK, resultValue); finish();
Tips: Saat Aktivitas konfigurasi Anda pertama kali dibuka, setel hasil Aktivitas ke RESULT_CANCELED, bersama dengan EXTRA_APPWIDGET_ID, seperti yang ditunjukkan dalam langkah ke-5 di atas. Dengan begitu, jika pengguna keluar dari Aktivitas sebelum berakhir, host Widget Aplikasi mendapatkan notifikasi bahwa konfigurasi telah dibatalkan dan Widget Aplikasi tidak akan ditambahkan.
Lihat contoh class ExampleAppWidgetConfigure.java dalam ApiDemos sebagai contoh.
Menyetel Gambar Pratinjau
Android 3.0 memperkenalkan kolom previewImage
, yang menentukan
pratinjau tampilan widget aplikasi tersebut. Pratinjau ini ditampilkan kepada pengguna dari
alat pilih widget. Jika kolom tidak didukung, ikon widget aplikasi akan digunakan untuk
pratinjau.
Berikut adalah cara menetapkan setelan ini di XML:
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" ... android:previewImage="@drawable/preview"> </appwidget-provider>
Untuk membantu membuat gambar pratinjau untuk widget aplikasi Anda (yang akan ditetapkan di kolom previewImage
), Android
Emulator dilengkapi dengan aplikasi yang bernama "Widget Preview." Untuk membuat
gambar pratinjau, luncurkan aplikasi ini, pilih widget aplikasi untuk
aplikasi Anda, lalu atur tampilan gambar pratinjau yang diinginkan, kemudian simpan
dan tempatkan pada resource aplikasi yang dapat digambar.
Menggunakan Widget Aplikasi dengan Koleksi.
Android 3.0 memperkenalkan widget aplikasi dengan koleksi. Jenis Widget
Aplikasi ini menggunakan RemoteViewsService
untuk menampilkan koleksi
yang didukung oleh data jarak jauh, seperti dari penyedia
konten. Data yang disediakan oleh RemoteViewsService
ditampilkan pada widget aplikasi menggunakan salah satu jenis tampilan berikut, yang
akan disebut sebagai "tampilan koleksi":
ListView
- Tampilan yang menunjukkan item dalam daftar scroll vertikal. Misalnya, lihat widget aplikasi Gmail.
GridView
- Tampilan yang menunjukkan item dalam petak scroll dua dimensi. Misalnya, lihat widget aplikasi Bookmark.
StackView
- Tampilan kartu yang ditumpuk (kurang lebih seperti rolodex), tempat pengguna dapat menjentikkan kartu depan ke atas/bawah untuk melihat kartu sebelum/selanjutnya berturut-turut. Contohnya seperti widget aplikasi YouTube dan Buku.
AdapterViewFlipper
ViewAnimator
sederhana yang didukung adaptor yang memberi animasi di antara dua tampilan atau lebih. Hanya satu turunan yang akan ditampilkan pada satu waktu.
Seperti yang dijelaskan di atas, tampilan koleksi ini menampilkan koleksi yang didukung oleh
data jarak jauh. Hal ini berarti tampilan tersebut menggunakan Adapter
untuk mengikat
antarmuka pengguna ke data. Adapter
mengikat
masing-masing item dari kumpulan data ke masing-masing objek View
.
Karena tampilan koleksi ini didukung oleh adaptor, framework Android
harus menyertakan arsitektur tambahan untuk mendukung penggunaannya dalam widget aplikasi. Dalam
konteks widget aplikasi, Adapter
digantikan oleh
RemoteViewsFactory
,
yang merupakan wrapper tipis di seluruh antarmuka
Adapter
.
Saat
item tertentu diminta dari koleksi, RemoteViewsFactory
membuat
dan menampilkan item ke koleksi sebagai objek
RemoteViews
.
Untuk menyertakan tampilan koleksi ke widget aplikasi, Anda
harus menerapkan RemoteViewsService
dan RemoteViewsFactory
.
RemoteViewsService
adalah layanan yang memungkinkan
adaptor jarak jauh meminta objek RemoteViews
. RemoteViewsFactory
adalah
antarmuka untuk adaptor di antara tampilan koleksi (seperti ListView
, GridView
, dan lainnya) dan
data yang mendasari untuk tampilan tersebut. Dari
contoh StackWidget, berikut adalah contoh kode boilerplate yang dapat digunakan untuk menerapkan
layanan dan antarmuka ini:
Kotlin
class StackWidgetService : RemoteViewsService() { override fun onGetViewFactory(intent: Intent): RemoteViewsFactory { return StackRemoteViewsFactory(this.applicationContext, intent) } } class StackRemoteViewsFactory( private val context: Context, intent: Intent ) : RemoteViewsService.RemoteViewsFactory { //... include adapter-like methods here. See the StackView Widget sample. }
Java
public class StackWidgetService extends RemoteViewsService { @Override public RemoteViewsFactory onGetViewFactory(Intent intent) { return new StackRemoteViewsFactory(this.getApplicationContext(), intent); } } class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory { //... include adapter-like methods here. See the StackView Widget sample. }
Contoh aplikasi
Nukilan kode dalam bagian ini diambil dari contoh StackWidget:
Contoh ini terdiri dari tumpukan 10 tampilan yang menampilkan nilai
"0!"
melalui "9!"
. Contoh
widget aplikasi memiliki perilaku utama berikut:
- Pengguna dapat melempar tampilan atas secara vertikal di widget aplikasi untuk membuka tampilan selanjutnya atau sebelumnya. Ini adalah perilaku StackView bawaan.
- Tanpa adanya interaksi pengguna, widget aplikasi akan secara otomatis membuka
tampilan
berikutnya secara berurutan, seperti slide show. Perilaku ini disebabkan oleh setelan
android:autoAdvanceViewId="@id/stack_view"
dalam fileres/xml/stackwidgetinfo.xml
. Setelan ini berlaku untuk ID tampilan, yang pada kasus ini merupakan ID tampilan dari tampilan bertumpuk. - Jika pengguna menyentuh tampilan atas, widget aplikasi menampilkan pesan
Toast
"Tampilan tersentuh n," dengan n adalah indeks (posisi) tampilan tersentuh. Untuk pembahasan lebih lanjut tentang cara penerapannya, lihat Menambahkan perilaku ke masing-masing item.
Menerapkan widget aplikasi dengan koleksi
Untuk menerapkan widget aplikasi dengan koleksi, ikuti langkah-langkah dasar yang sama seperti yang Anda gunakan untuk menerapkan widget aplikasi lain. Bagian berikut menjelaskan langkah tambahan yang perlu dilakukan untuk menerapkan widget aplikasi dengan koleksi.
Manifes untuk widget aplikasi dengan koleksi
Selain persyaratan yang tercantum dalam Mendeklarasikan
widget aplikasi di Manifes, untuk memungkinkan widget aplikasi dengan
koleksi mengikat ke RemoteViewsService
, Anda harus
mendeklarasikan layanan dalam file manifes Anda dengan BIND_REMOTEVIEWS
izin. Tindakan ini akan mencegah aplikasi
mengakses data widget aplikasi Anda secara bebas. Misalnya, saat membuat Widget
Aplikasi yang menggunakan RemoteViewsService
untuk mengisi
tampilan koleksi, entri manifes akan terlihat seperti ini:
<service android:name="MyWidgetService" ... android:permission="android.permission.BIND_REMOTEVIEWS" />
Baris android:name="MyWidgetService"
mengacu pada subclass RemoteViewsService
Anda.
Tata letak untuk widget aplikasi dengan koleksi
Persyaratan utama untuk file XML tata letak widget aplikasi Anda adalah
berisi salah satu tampilan koleksi: ListView
,
GridView
, StackView
, atau
AdapterViewFlipper
. Berikut adalah widget_layout.xml
untuk
contoh StackWidget:
<?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <StackView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/stack_view" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:loopViews="true" /> <TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/empty_view" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center" android:background="@drawable/widget_item_background" android:textColor="#ffffff" android:textStyle="bold" android:text="@string/empty_view_text" android:textSize="20sp" /> </FrameLayout>
Perlu diingat bahwa tampilan kosong harus berhubungan dengan tampilan koleksi yang memiliki tampilan kosong yang mewakili status kosong.
Selain file tata letak untuk seluruh widget aplikasi, Anda harus membuat
file tata letak lain yang menentukan tata letak setiap item dalam koleksi (misalnya,
tata letak untuk setiap buku dalam koleksi buku).
Contoh
StackWidget hanya memiliki satu file tata letak, widget_item.xml
, karena
semua item menggunakan tata letak yang sama.
Class AppWidgetProvider untuk widget aplikasi dengan koleksi
Seperti widget aplikasi biasa, sebagian besar kode dalam subclass AppWidgetProvider
biasanya muncul di onUpdate()
. Perbedaan besar pada
penerapan untuk onUpdate()
saat membuat widget
aplikasi dengan koleksi adalah Anda harus memanggil setRemoteAdapter()
. Tindakan ini akan memberi tahu
tampilan koleksi tempat untuk mendapatkan datanya. Kemudian, RemoteViewsService
dapat menampilkan penerapan RemoteViewsFactory
Anda, dan
widget dapat menyajikan data yang sesuai. Saat memanggil metode ini, Anda
harus meneruskan intent yang mengarah ke penerapan RemoteViewsService
dan ID widget aplikasi yang menentukan widget
aplikasi yang akan diupdate.
Misalnya, berikut adalah cara contoh StackWidget menerapkan metode callback onUpdate()
untuk menetapkan
RemoteViewsService
sebagai adaptor jarak jauh untuk koleksi widget
aplikasi:
Kotlin
override fun onUpdate( context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray ) { // update each of the app widgets with the remote adapter appWidgetIds.forEach { appWidgetId -> // Set up the intent that starts the StackViewService, which will // provide the views for this collection. val intent = Intent(context, StackWidgetService::class.java).apply { // Add the app widget ID to the intent extras. putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) data = Uri.parse(toUri(Intent.URI_INTENT_SCHEME)) } // Instantiate the RemoteViews object for the app widget layout. val rv = RemoteViews(context.packageName, R.layout.widget_layout).apply { // Set up the RemoteViews object to use a RemoteViews adapter. // This adapter connects // to a RemoteViewsService through the specified intent. // This is how you populate the data. setRemoteAdapter(R.id.stack_view, intent) // The empty view is displayed when the collection has no items. // It should be in the same layout used to instantiate the RemoteViews // object above. setEmptyView(R.id.stack_view, R.id.empty_view) } // // Do additional processing specific to this app widget... // appWidgetManager.updateAppWidget(appWidgetId, rv) } super.onUpdate(context, appWidgetManager, appWidgetIds) }
Java
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { // update each of the app widgets with the remote adapter for (int i = 0; i < appWidgetIds.length; ++i) { // Set up the intent that starts the StackViewService, which will // provide the views for this collection. Intent intent = new Intent(context, StackWidgetService.class); // Add the app widget ID to the intent extras. intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]); intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME))); // Instantiate the RemoteViews object for the app widget layout. RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_layout); // Set up the RemoteViews object to use a RemoteViews adapter. // This adapter connects // to a RemoteViewsService through the specified intent. // This is how you populate the data. rv.setRemoteAdapter(R.id.stack_view, intent); // The empty view is displayed when the collection has no items. // It should be in the same layout used to instantiate the RemoteViews // object above. rv.setEmptyView(R.id.stack_view, R.id.empty_view); // // Do additional processing specific to this app widget... // appWidgetManager.updateAppWidget(appWidgetIds[i], rv); } super.onUpdate(context, appWidgetManager, appWidgetIds); }
Class RemoteViewsService
Mempertahankan data
Seperti yang dijelaskan di atas, subclass RemoteViewsService
Anda
menyediakan RemoteViewsFactory
yang digunakan untuk mengisi tampilan koleksi jarak jauh.
Secara khusus, Anda perlu melakukan langkah-langkah berikut:
- Subclass
RemoteViewsService
.RemoteViewsService
adalah layanan yang dapat digunakan adaptor jarak jauh untuk membuat permintaanRemoteViews
. - Dalam subclass
RemoteViewsService
, sertakan class yang menerapkan antarmukaRemoteViewsFactory
.RemoteViewsFactory
adalah antarmuka untuk adaptor antar tampilan koleksi jarak (sepertiListView
,GridView
, dan lainnya) dan data yang mendasari untuk tampilan tersebut. Penerapan Anda bertujuan untuk membuat objekRemoteViews
bagi setiap item dalam set data. Antarmuka ini adalah wrapper tipis di seluruhAdapter
.
Anda tidak dapat bergantung pada satu instance layanan, atau data apa pun yang
ditampung, untuk dipertahankan. Oleh karena itu, sebaiknya Anda tidak menyimpan data dalam
RemoteViewsService
(kecuali jika data bersifat statis).
Jika ingin
mempertahankan data widget aplikasi, cara terbaik adalah menggunakan
ContentProvider
yang datanya bertahan melalui
siklus hidup proses.
Konten utama penerapan RemoteViewsService
adalah RemoteViewsFactory
,
yang dijelaskan di bawah ini.
Antarmuka RemoteViewFactory
Class kustom Anda yang menerapkan antarmuka RemoteViewsFactory
memberikan data kepada widget aplikasi untuk item dalam koleksinya.
Untuk
melakukannya, class ini menggabungkan file tata letak XML item widget aplikasi Anda dengan sumber data.
Sumber data ini dapat berupa apa saja, dari database hingga array sederhana. Dalam
contoh StackWidget, sumber data merupakan array WidgetItems
. RemoteViewsFactory
berfungsi sebagai
adaptor untuk menyatukan data pada tampilan koleksi jarak jauh.
Dua metode terpenting yang perlu diterapkan untuk
subclass RemoteViewsFactory
adalah
onCreate()
dan
getViewAt()
.
Sistem memanggil onCreate()
saat
membuat pabrik untuk pertama kalinya. Pada tahap ini, Anda perlu menyiapkan
koneksi dan/atau kursor untuk sumber data. Misalnya,
contoh StackWidget menggunakan onCreate()
untuk menginisialisasi array objek WidgetItem
. Saat widget aplikasi aktif,
sistem mengakses objek ini menggunakan posisi indeks objek dalam array dan teks di dalamnya akan
ditampilkan.
Berikut adalah nukilan dari
penerapan RemoteViewsFactory
contoh StackWidget
yang menunjukkan sebagian
metode onCreate()
:
Kotlin
private const val REMOTE_VIEW_COUNT: Int = 10 class StackRemoteViewsFactory( private val context: Context ) : RemoteViewsService.RemoteViewsFactory { private lateinit var widgetItems: List<WidgetItem> override fun onCreate() { // In onCreate() you setup any connections / cursors to your data source. Heavy lifting, // for example downloading or creating content etc, should be deferred to onDataSetChanged() // or getViewAt(). Taking more than 20 seconds in this call will result in an ANR. widgetItems = List(REMOTE_VIEW_COUNT) { index -> WidgetItem("$index!") } ... } ... }
Java
class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory { private static final int count = 10; private List<WidgetItem> widgetItems = new ArrayList<WidgetItem>(); private Context context; private int appWidgetId; public StackRemoteViewsFactory(Context context, Intent intent) { this.context = context; appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); } public void onCreate() { // In onCreate() you setup any connections / cursors to your data source. Heavy lifting, // for example downloading or creating content etc, should be deferred to onDataSetChanged() // or getViewAt(). Taking more than 20 seconds in this call will result in an ANR. for (int i = 0; i < count; i++) { widgetItems.add(new WidgetItem(i + "!")); } ... } ...
Metode RemoteViewsFactory
getViewAt()
menampilkan objek RemoteViews
yang sesuai dengan data pada
position
khusus dalam set data. Berikut adalah nukilan dari
penerapan RemoteViewsFactory
contoh
StackWidget:
Kotlin
override fun getViewAt(position: Int): RemoteViews { // Construct a remote views item based on the app widget item XML file, // and set the text based on the position. return RemoteViews(context.packageName, R.layout.widget_item).apply { setTextViewText(R.id.widget_item, widgetItems[position].text) } }
Java
public RemoteViews getViewAt(int position) { // Construct a remote views item based on the app widget item XML file, // and set the text based on the position. RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_item); rv.setTextViewText(R.id.widget_item, widgetItems.get(position).text); ... // Return the remote views object. return rv; }
Menambahkan perilaku pada masing-masing item
Bagian di atas menunjukkan cara mengikat data ke koleksi widget aplikasi. Namun, bagaimana jika Anda ingin menambahkan perilaku dinamis ke masing-masing item dalam tampilan koleksi?
Seperti yang dijelaskan dalam Menggunakan Class AppWidgetProvider
, biasanya Anda menggunakan setOnClickPendingIntent()
untuk menyetel
perilaku klik objek—seperti untuk membuat tombol meluncurkan Activity
. Namun, cara ini tidak diizinkan untuk tampilan turunan dalam
masing-masing item koleksi (untuk memperjelas, Anda dapat menggunakan setOnClickPendingIntent()
untuk menyiapkan tombol global
dalam widget aplikasi Gmail yang meluncurkan aplikasinya, tetapi tidak pada
masing-masing item daftar). Untuk menambahkan perilaku klik pada masing-masing item dalam
koleksi, Anda dapat menggunakan setOnClickFillInIntent()
. Dengan kata lain, Anda harus menyiapkan tata letak intent yang tertunda
untuk tampilan koleksi, lalu menyetel intent pengganti pada setiap item dalam
koleksi melalui RemoteViewsFactory
.
Bagian ini menggunakan
contoh StackWidget untuk menjelaskan cara menambahkan perilaku pada masing-masing item. Dalam
contoh StackWidget, jika pengguna menyentuh tampilan atas, widget aplikasi menampilkan
pesan Toast
"Tampilan tersentuh n," dengan n adalah
indeks (posisi) tampilan tersentuh. Begini caranya:
StackWidgetProvider
(subclassAppWidgetProvider
) membuat intent tertunda yang memiliki tindakan kustom bernamaTOAST_ACTION
.- Saat pengguna menyentuh tampilan, intent akan diaktifkan dan mulai menyiarkan
TOAST_ACTION
. - Siaran ini ditangkap oleh metode
onReceive()
dariStackWidgetProvider
, dan widget aplikasi menampilkan pesanToast
untuk tampilan tersentuh. Data untuk item koleksi diberikan olehRemoteViewsFactory
, melaluiRemoteViewsService
.
Catatan: Contoh StackWidget menggunakan siaran, tetapi biasanya widget aplikasi hanya akan meluncurkan aktivitas dalam skenario seperti ini.
Menyiapkan template intent yang tertunda
StackWidgetProvider
(subclass AppWidgetProvider
) menyiapkan intent yang tertunda.
Masing-masing item koleksi tidak dapat menyiapkan intent tertunda sendiri.
Sebaliknya, koleksi secara keseluruhan dapat menyiapkan template intent yang tertunda, dan
masing-masing item menyetel intent pengganti untuk membuat perilaku
unik
per item.
Class ini juga menerima siaran yang dikirim saat pengguna menyentuh
tampilan. Hal ini memproses peristiwa ini pada onReceive()
metodenya. Jika tindakan intent adalah
TOAST_ACTION
, widget aplikasi menampilkan pesan Toast
untuk tampilan saat ini.
Kotlin
const val TOAST_ACTION = "com.example.android.stackwidget.TOAST_ACTION" const val EXTRA_ITEM = "com.example.android.stackwidget.EXTRA_ITEM" class StackWidgetProvider : AppWidgetProvider() { ... // Called when the BroadcastReceiver receives an Intent broadcast. // Checks to see whether the intent's action is TOAST_ACTION. If it is, the app widget // displays a Toast message for the current item. override fun onReceive(context: Context, intent: Intent) { val mgr: AppWidgetManager = AppWidgetManager.getInstance(context) if (intent.action == TOAST_ACTION) { val appWidgetId: Int = intent.getIntExtra( AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID ) val viewIndex: Int = intent.getIntExtra(EXTRA_ITEM, 0) Toast.makeText(context, "Touched view $viewIndex", Toast.LENGTH_SHORT).show() } super.onReceive(context, intent) } override fun onUpdate( context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray ) { // update each of the app widgets with the remote adapter appWidgetIds.forEach { appWidgetId -> // Sets up the intent that points to the StackViewService that will // provide the views for this collection. val intent = Intent(context, StackWidgetService::class.java).apply { putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) // When intents are compared, the extras are ignored, so we need to embed the extras // into the data so that the extras will not be ignored. data = Uri.parse(toUri(Intent.URI_INTENT_SCHEME)) } val rv = RemoteViews(context.packageName, R.layout.widget_layout).apply { setRemoteAdapter(R.id.stack_view, intent) // The empty view is displayed when the collection has no items. It should be a // sibling of the collection view. setEmptyView(R.id.stack_view, R.id.empty_view) } // This section makes it possible for items to have individualized behavior. // It does this by setting up a pending intent template. Individuals items of a // collection cannot set up their own pending intents. Instead, the collection as a // whole sets up a pending intent template, and the individual items set a fillInIntent // to create unique behavior on an item-by-item basis. val toastPendingIntent: PendingIntent = Intent( context, StackWidgetProvider::class.java ).run { // Set the action for the intent. // When the user touches a particular view, it will have the effect of // broadcasting TOAST_ACTION. action = TOAST_ACTION putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId) data = Uri.parse(toUri(Intent.URI_INTENT_SCHEME)) PendingIntent.getBroadcast(context, 0, this, PendingIntent.FLAG_UPDATE_CURRENT) } rv.setPendingIntentTemplate(R.id.stack_view, toastPendingIntent) appWidgetManager.updateAppWidget(appWidgetId, rv) } super.onUpdate(context, appWidgetManager, appWidgetIds) } }
Java
public class StackWidgetProvider extends AppWidgetProvider { public static final String TOAST_ACTION = "com.example.android.stackwidget.TOAST_ACTION"; public static final String EXTRA_ITEM = "com.example.android.stackwidget.EXTRA_ITEM"; ... // Called when the BroadcastReceiver receives an Intent broadcast. // Checks to see whether the intent's action is TOAST_ACTION. If it is, the app widget // displays a Toast message for the current item. @Override public void onReceive(Context context, Intent intent) { AppWidgetManager mgr = AppWidgetManager.getInstance(context); if (intent.getAction().equals(TOAST_ACTION)) { int appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); int viewIndex = intent.getIntExtra(EXTRA_ITEM, 0); Toast.makeText(context, "Touched view " + viewIndex, Toast.LENGTH_SHORT).show(); } super.onReceive(context, intent); } @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { // update each of the app widgets with the remote adapter for (int i = 0; i < appWidgetIds.length; ++i) { // Sets up the intent that points to the StackViewService that will // provide the views for this collection. Intent intent = new Intent(context, StackWidgetService.class); intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]); // When intents are compared, the extras are ignored, so we need to embed the extras // into the data so that the extras will not be ignored. intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME))); RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_layout); rv.setRemoteAdapter(appWidgetIds[i], R.id.stack_view, intent); // The empty view is displayed when the collection has no items. It should be a sibling // of the collection view. rv.setEmptyView(R.id.stack_view, R.id.empty_view); // This section makes it possible for items to have individualized behavior. // It does this by setting up a pending intent template. Individuals items of a collection // cannot set up their own pending intents. Instead, the collection as a whole sets // up a pending intent template, and the individual items set a fillInIntent // to create unique behavior on an item-by-item basis. Intent toastIntent = new Intent(context, StackWidgetProvider.class); // Set the action for the intent. // When the user touches a particular view, it will have the effect of // broadcasting TOAST_ACTION. toastIntent.setAction(StackWidgetProvider.TOAST_ACTION); toastIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetIds[i]); intent.setData(Uri.parse(intent.toUri(Intent.URI_INTENT_SCHEME))); PendingIntent toastPendingIntent = PendingIntent.getBroadcast(context, 0, toastIntent, PendingIntent.FLAG_UPDATE_CURRENT); rv.setPendingIntentTemplate(R.id.stack_view, toastPendingIntent); appWidgetManager.updateAppWidget(appWidgetIds[i], rv); } super.onUpdate(context, appWidgetManager, appWidgetIds); } }
Menyetel Intent pengganti
RemoteViewsFactory
harus menyetel intent pengganti pada setiap item dalam koleksi.
Hal ini memungkinkan untuk memisahkan masing-masing tindakan klik dari
item tersebut. Kemudian, intent pengganti digabungkan dengan template PendingIntent
untuk menentukan intent akhir yang
akan dijalankan saat item diklik.
Kotlin
private const val REMOTE_VIEW_COUNT: Int = 10 class StackRemoteViewsFactory( private val context: Context, intent: Intent ) : RemoteViewsService.RemoteViewsFactory { private lateinit var widgetItems: List<WidgetItem> private val appWidgetId: Int = intent.getIntExtra( AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID ) override fun onCreate() { // In onCreate() you setup any connections / cursors to your data source. Heavy lifting, // for example downloading or creating content etc, should be deferred to onDataSetChanged() // or getViewAt(). Taking more than 20 seconds in this call will result in an ANR. widgetItems = List(REMOTE_VIEW_COUNT) { index -> WidgetItem("$index!") } ... } ... override fun getViewAt(position: Int): RemoteViews { // Construct a remote views item based on the app widget item XML file, // and set the text based on the position. return RemoteViews(context.packageName, R.layout.widget_item).apply { setTextViewText(R.id.widget_item, widgetItems[position].text) // Next, set a fill-intent, which will be used to fill in the pending intent template // that is set on the collection view in StackWidgetProvider. val fillInIntent = Intent().apply { Bundle().also { extras -> extras.putInt(EXTRA_ITEM, position) putExtras(extras) } } // Make it possible to distinguish the individual on-click // action of a given item setOnClickFillInIntent(R.id.widget_item, fillInIntent) ... } } ... }
Java
public class StackWidgetService extends RemoteViewsService { @Override public RemoteViewsFactory onGetViewFactory(Intent intent) { return new StackRemoteViewsFactory(this.getApplicationContext(), intent); } } class StackRemoteViewsFactory implements RemoteViewsService.RemoteViewsFactory { private static final int count = 10; private List<WidgetItem> widgetItems = new ArrayList<WidgetItem>(); private Context context; private int appWidgetId; public StackRemoteViewsFactory(Context context, Intent intent) { this.context = context; appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID); } // Initialize the data set. public void onCreate() { // In onCreate() you set up any connections / cursors to your data source. Heavy lifting, // for example downloading or creating content etc, should be deferred to onDataSetChanged() // or getViewAt(). Taking more than 20 seconds in this call will result in an ANR. for (int i = 0; i < count; i++) { widgetItems.add(new WidgetItem(i + "!")); } ... } ... // Given the position (index) of a WidgetItem in the array, use the item's text value in // combination with the app widget item XML file to construct a RemoteViews object. public RemoteViews getViewAt(int position) { // position will always range from 0 to getCount() - 1. // Construct a RemoteViews item based on the app widget item XML file, and set the // text based on the position. RemoteViews rv = new RemoteViews(context.getPackageName(), R.layout.widget_item); rv.setTextViewText(R.id.widget_item, widgetItems.get(position).text); // Next, set a fill-intent, which will be used to fill in the pending intent template // that is set on the collection view in StackWidgetProvider. Bundle extras = new Bundle(); extras.putInt(StackWidgetProvider.EXTRA_ITEM, position); Intent fillInIntent = new Intent(); fillInIntent.putExtras(extras); // Make it possible to distinguish the individual on-click // action of a given item rv.setOnClickFillInIntent(R.id.widget_item, fillInIntent); ... // Return the RemoteViews object. return rv; } ... }
Terus Memperbarui Data Koleksi
Gambar berikut menunjukkan alur yang terjadi dalam widget aplikasi yang
menggunakan
koleksi saat update dilakukan. Hal ini menunjukkan cara kode widget aplikasi berinteraksi dengan
RemoteViewsFactory
, dan cara Anda dapat memicu update:

Salah satu fitur widget aplikasi yang menggunakan koleksi adalah opsi untuk menyediakan
konten terbaru bagi pengguna. Misalnya, perhatikan widget aplikasi Gmail Android 3.0,
yang memberikan ringkasan kotak masuk kepada pengguna. Untuk melakukannya,
Anda harus dapat memicu RemoteViewsFactory
dan
tampilan koleksi untuk mendapatkan dan menampilkan data baru. Anda dapat melakukannya dengan panggilan AppWidgetManager
notifyAppWidgetViewDataChanged()
. Panggilan ini menampilkan callback pada
metode onDataSetChanged()
dari RemoteViewsFactory
, yang memberi Anda peluang untuk mendapatkan
data baru. Perlu diingat bahwa Anda dapat menjalankan
operasi pemrosesan intensif secara sinkron dalam callback onDataSetChanged()
. Dipastikan bahwa panggilan ini akan
selesai sebelum metadata atau data tampilan diambil dari RemoteViewsFactory
. Selain itu,
Anda dapat menjalankan operasi pemrosesan intensif dalam
metode getViewAt()
. Jika panggilan ini perlu waktu lama, tampilan yang dimuat (yang ditetapkan oleh
metode getLoadingView()
dari RemoteViewsFactory
)
akan ditampilkan dalam posisi yang sesuai dengan tampilan koleksi hingga
kembali.