หน้านี้จะอธิบายแนวทางปฏิบัติที่แนะนำในการสร้างวิดเจ็ตขั้นสูงขึ้นสำหรับ ประสบการณ์ของผู้ใช้ที่ดีขึ้น
การเพิ่มประสิทธิภาพสำหรับการอัปเดตเนื้อหาวิดเจ็ต
การอัปเดตเนื้อหาของวิดเจ็ตอาจทำให้การคํานวณมีค่าใช้จ่ายสูง เพื่อประหยัดแบตเตอรี่ รวมถึงเพิ่มประสิทธิภาพของประเภท ความถี่ และช่วงเวลาของการอัปเดต
ประเภทการอัปเดตวิดเจ็ต
คุณสามารถอัปเดตวิดเจ็ตได้ 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()
ซึ่งคุณวางโค้ดเพื่ออัปเดตวิดเจ็ตได้ อย่างไรก็ตาม ให้พิจารณาทางเลือกในการอัปเดตตัวรับสัญญาณการออกอากาศที่อธิบายไว้ในส่วนถัดไป หากวิดเจ็ตต้องโหลดข้อมูลแบบไม่พร้อมกันหรือใช้เวลาอัปเดตนานกว่า 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
อย่างไรก็ตาม เนื่องจากการดำเนินการนี้ใช้ทรัพยากรของอุปกรณ์มาก โปรดใช้ความระมัดระวังและรอฟัง
ไปยังการออกอากาศที่เฉพาะเจาะจงเท่านั้น ด้วยการเปิดตัว broadcast
ข้อจำกัดใน 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>