หน้านี้จะอธิบายถึงแนวทางปฏิบัติแนะนำในการสร้างวิดเจ็ตขั้นสูงขึ้นเพื่อประสบการณ์ของผู้ใช้ที่ดียิ่งขึ้น
การเพิ่มประสิทธิภาพสำหรับการอัปเดตเนื้อหาวิดเจ็ต
การอัปเดตเนื้อหาวิดเจ็ตอาจใช้พลังงานในการประมวลผลมาก หากต้องการประหยัดแบตเตอรี่ ให้เพิ่มประสิทธิภาพประเภท ความถี่ และเวลาการอัปเดต
ประเภทการอัปเดตวิดเจ็ต
คุณสามารถอัปเดตวิดเจ็ตได้ 3 วิธี ได้แก่ การอัปเดตแบบเต็ม การอัปเดตบางส่วน และการรีเฟรชข้อมูลในกรณีวิดเจ็ตคอลเล็กชัน แต่ละวิธีมีต้นทุนการประมวลผลและผลลัพธ์ที่แตกต่างกัน
ข้อมูลต่อไปนี้จะอธิบายการอัปเดตแต่ละประเภทและแสดงข้อมูลโค้ดของแต่ละประเภท
การอัปเดตเต็มรูปแบบ: โทรติดต่อ
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
ที่ระบุไว้ก่อนหน้านี้ ระบบจะไม่สนใจวิธีการนี้หากวิดเจ็ตไม่ได้รับการอัปเดตเต็มรูปแบบอย่างน้อย 1 ครั้งผ่าน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
วิดเจ็ตสามารถอัปเดตตามการโต้ตอบของผู้ใช้ ออกอากาศข้อมูลอัปเดต หรือทั้ง 2 อย่าง
อัปเดตเป็นระยะๆ
คุณควบคุมความถี่ของการอัปเดตเป็นระยะได้โดยระบุค่าสำหรับ AppWidgetProviderInfo.updatePeriodMillis
ใน appwidget-provider
XML การอัปเดตแต่ละครั้งจะทริกเกอร์เมธอด AppWidgetProvider.onUpdate()
ซึ่งคุณวางโค้ดเพื่ออัปเดตวิดเจ็ตได้ อย่างไรก็ตาม ลองพิจารณาทางเลือกสำหรับการอัปเดต Broadcast Receiver ที่อธิบายไว้ในส่วนต่อไปนี้หากวิดเจ็ตต้องโหลดข้อมูลแบบไม่พร้อมกันหรือใช้เวลานานกว่า 10 วินาทีในการอัปเดต เพราะหลังจากผ่านไป 10 วินาที ระบบจะพิจารณาว่า BroadcastReceiver
ไม่ตอบสนอง
updatePeriodMillis
ไม่รองรับค่าที่น้อยกว่า 30 นาที แต่หากต้องการปิดใช้การอัปเดตเป็นระยะ ให้ระบุ 0
คุณสามารถอนุญาตให้ผู้ใช้ปรับความถี่ในการอัปเดตในการกําหนดค่าได้ เช่น ผู้ชมอาจต้องการให้ข้อมูลราคาหุ้นอัปเดตทุก 15 นาทีหรือวันละ 4 ครั้งเท่านั้น ในกรณีนี้ ให้ตั้งค่า updatePeriodMillis
เป็น 0 และใช้ WorkManager
แทน
การอัปเดตเพื่อตอบสนองต่อการโต้ตอบของผู้ใช้
ต่อไปนี้เป็นวิธีแนะนำในการอัปเดตวิดเจ็ตตามการโต้ตอบของผู้ใช้
จากกิจกรรมของแอป: เรียกใช้
AppWidgetManager.updateAppWidget
โดยตรงเพื่อตอบสนองต่อการโต้ตอบของผู้ใช้ เช่น การแตะจากการโต้ตอบระยะไกล เช่น การแจ้งเตือนหรือวิดเจ็ตแอป ให้สร้าง
PendingIntent
แล้วอัปเดตวิดเจ็ตจากActivity
,Broadcast
หรือService
ที่เรียกใช้ คุณเลือกลําดับความสําคัญของคุณเองได้ เช่น หากเลือกBroadcast
สำหรับPendingIntent
คุณจะเลือกการออกอากาศที่ทำงานอยู่เบื้องหน้าเพื่อจัดลำดับความสำคัญของBroadcastReceiver
ได้
การอัปเดตเพื่อตอบสนองต่อเหตุการณ์การออกอากาศ
ตัวอย่างเหตุการณ์การออกอากาศที่กําหนดให้วิดเจ็ตอัปเดตคือเมื่อผู้ใช้ถ่ายภาพ ในกรณีนี้ คุณต้องการอัปเดตวิดเจ็ตเมื่อตรวจพบรูปภาพใหม่
คุณกำหนดเวลางานด้วย JobScheduler
และระบุการประกาศเป็นทริกเกอร์ได้โดยใช้เมธอด JobInfo.Builder.addTriggerContentUri
นอกจากนี้ คุณยังลงทะเบียน BroadcastReceiver
สำหรับออกอากาศได้ เช่น การฟัง ACTION_LOCALE_CHANGED
อย่างไรก็ตาม เนื่องจากฟีเจอร์นี้ใช้ทรัพยากรของอุปกรณ์ จึงควรใช้อย่างระมัดระวังและฟังเฉพาะการออกอากาศที่ต้องการ เมื่อมีข้อจำกัดเกี่ยวกับประกาศใน Android 7.0 (API ระดับ 24) และ Android 8.0 (API ระดับ 26) แอปจะลงทะเบียนประกาศโดยนัยในไฟล์ Manifest ไม่ได้ โดยมีข้อยกเว้นบางประการ
ข้อควรพิจารณาเมื่ออัปเดตวิดเจ็ตจาก BroadcastReceiver
หากวิดเจ็ตได้รับการอัปเดตจาก BroadcastReceiver
ซึ่งรวมถึง AppWidgetProvider
โปรดคำนึงถึงข้อควรพิจารณาต่อไปนี้เกี่ยวกับระยะเวลาและลำดับความสำคัญของการอัปเดตวิดเจ็ต
ระยะเวลาการอัปเดต
ตามกฎแล้ว ระบบจะอนุญาตให้ตัวรับการออกอากาศซึ่งมักจะทำงานในเธรดหลักของแอปทำงานได้สูงสุด 10 วินาทีก่อนที่จะถือว่าไม่ตอบสนองและทำให้เกิดข้อผิดพลาดแอปพลิเคชันไม่ตอบสนอง (ANR) หากการอัปเดตวิดเจ็ตใช้เวลานานขึ้น ให้ลองใช้ทางเลือกต่อไปนี้
กำหนดเวลางานโดยใช้
WorkManager
ให้ผู้รับมีเวลามากขึ้นในการใช้วิธี
goAsync
ซึ่งจะช่วยให้ผู้รับดำเนินการเป็นเวลา 30 วินาที
ดูข้อมูลเพิ่มเติมที่ข้อควรพิจารณาและแนวทางปฏิบัติแนะนำด้านความปลอดภัย
ลำดับความสำคัญของการอัปเดต
โดยค่าเริ่มต้น การออกอากาศต่างๆ รวมถึงรายการที่ดำเนินการโดยใช้ AppWidgetProvider.onUpdate
จะเรียกใช้เป็นกระบวนการเบื้องหลัง ซึ่งหมายความว่าทรัพยากรของระบบที่ทำงานหนักเกินไปอาจทำให้เกิดความล่าช้าในการเรียกใช้ตัวรับสัญญาณการออกอากาศ หากต้องการจัดลำดับความสำคัญการออกอากาศเป็นขั้นตอนเบื้องหน้า
เช่น เพิ่ม Flag Intent.FLAG_RECEIVER_FOREGROUND
ลงใน Intent
ที่ส่งไปยัง PendingIntent.getBroadcast
เมื่อผู้ใช้แตะส่วนใดส่วนหนึ่งของวิดเจ็ต
สร้างตัวอย่างที่ถูกต้องซึ่งมีรายการแบบไดนามิก
ส่วนนี้อธิบายวิธีที่แนะนำในการแสดงหลายรายการในตัวอย่างวิดเจ็ตสำหรับวิดเจ็ตที่มีมุมมองคอลเล็กชัน ซึ่งก็คือวิดเจ็ตที่ใช้ ListView
, GridView
หรือ StackView
หากวิดเจ็ตใช้มุมมองใดมุมมองหนึ่งเหล่านี้ การสร้างตัวอย่างที่ปรับขนาดได้โดยการระบุเลย์เอาต์วิดเจ็ตจริงโดยตรงจะลดประสบการณ์การใช้งานเมื่อตัวอย่างวิดเจ็ตไม่แสดงรายการใดๆ ซึ่งเกิดขึ้นเนื่องจากมีการตั้งค่าข้อมูลมุมมองคอลเล็กชันแบบไดนามิกที่รันไทม์ และมีลักษณะคล้ายกับรูปภาพที่แสดงในรูปที่ 1
หากต้องการให้ตัวอย่างวิดเจ็ตที่มีมุมมองคอลเล็กชันแสดงอย่างถูกต้องในเครื่องมือเลือกวิดเจ็ต เราขอแนะนำให้เก็บไฟล์เลย์เอาต์แยกต่างหากสำหรับตัวอย่างเท่านั้น ไฟล์เลย์เอาต์แยกต่างหากนี้ประกอบด้วยเลย์เอาต์วิดเจ็ตจริงและมุมมองคอลเล็กชันตัวยึดตําแหน่งที่มีรายการจำลอง เช่น คุณอาจจำลอง ListView
ได้โดยระบุตัวยึดตําแหน่ง 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
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="@string/fake_title" />
<TextView android:id="@id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/fake_content" />
</LinearLayout>
หากต้องการระบุรายการในรายการจำลอง คุณสามารถใส่เลย์เอาต์หลายครั้งได้ แต่จะทำให้รายการในรายการแต่ละรายการเหมือนกัน ในการระบุรายการ ที่ไม่ซ้ำกัน ให้ทำตามขั้นตอนต่อไปนี้
สร้างชุดแอตทริบิวต์สำหรับค่าข้อความ
<resources> <attr name="widgetTitle" format="string" /> <attr name="widgetContent" format="string" /> </resources>
ใช้แอตทริบิวต์ต่อไปนี้เพื่อตั้งค่าข้อความ
<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>
สร้างสไตล์ได้มากเท่าที่ต้องการสำหรับตัวอย่างเพลง กำหนดค่าใหม่ในแต่ละรูปแบบ
<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>
ใช้รูปแบบกับรายการจำลองในเลย์เอาต์ตัวอย่าง โดยทำดังนี้
<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>