สร้างวิดเจ็ตแบบง่าย

ลองใช้ Compose
Jetpack Compose เป็นชุดเครื่องมือ UI ที่แนะนำสำหรับ Android ดูวิธีสร้างวิดเจ็ตโดยใช้ API รูปแบบ Compose

วิดเจ็ตแอปคือมุมมองแอปขนาดย่อที่คุณฝังในแอปอื่นๆ ได้ เช่น หน้าจอหลัก และรับการอัปเดตเป็นระยะๆ มุมมองเหล่านี้เรียกว่าวิดเจ็ตในอินเทอร์เฟซผู้ใช้ และคุณสามารถเผยแพร่มุมมองดังกล่าวได้โดยใช้ผู้ให้บริการวิดเจ็ตแอป (หรือผู้ให้บริการวิดเจ็ต) ส่วนประกอบของแอปที่ มีวิดเจ็ตอื่นๆ เรียกว่าโฮสต์วิดเจ็ตแอป (หรือโฮสต์วิดเจ็ต) รูปที่ 1 แสดงตัวอย่างวิดเจ็ตเพลง

ตัวอย่างวิดเจ็ตเพลง
รูปที่ 1 ตัวอย่างวิดเจ็ตเพลง

เอกสารนี้อธิบายวิธีเผยแพร่วิดเจ็ตโดยใช้ผู้ให้บริการวิดเจ็ต โปรดดูรายละเอียดเกี่ยวกับการสร้าง AppWidgetHost ของคุณเองเพื่อโฮสต์วิดเจ็ตแอปที่หัวข้อสร้างโฮสต์วิดเจ็ต

ดูข้อมูลเกี่ยวกับวิธีออกแบบวิดเจ็ตได้ที่ภาพรวมวิดเจ็ตแอป

คอมโพเนนต์วิดเจ็ต

หากต้องการสร้างวิดเจ็ต คุณต้องมีคอมโพเนนต์พื้นฐานต่อไปนี้

ออบเจ็กต์ AppWidgetProviderInfo
อธิบายข้อมูลเมตาของวิดเจ็ต เช่น เลย์เอาต์ของวิดเจ็ต ความถี่ในการอัปเดต และคลาส AppWidgetProvider AppWidgetProviderInfo กำหนดไว้ใน XML ตามที่อธิบายไว้ในเอกสารนี้
AppWidgetProvider ชั้นเรียน
กำหนดวิธีการพื้นฐานที่ช่วยให้คุณเชื่อมต่อกับวิดเจ็ตโดยใช้โปรแกรมได้ โดยคุณจะได้รับการออกอากาศเมื่อมีการอัปเดต เปิดใช้ ปิดใช้ หรือลบวิดเจ็ต คุณประกาศ AppWidgetProvider ใน ไฟล์ Manifest แล้วติดตั้งใช้งานตามที่ อธิบายไว้ในเอกสารนี้
ดูเลย์เอาต์
กำหนดเลย์เอาต์เริ่มต้นสำหรับวิดเจ็ต เลย์เอาต์กำหนดไว้ใน XML ตามที่อธิบายไว้ในเอกสารนี้

รูปที่ 2 แสดงให้เห็นว่าคอมโพเนนต์เหล่านี้เข้ากับโฟลว์การประมวลผลวิดเจ็ตแอปโดยรวมได้อย่างไร

ขั้นตอนการประมวลผลวิดเจ็ตของแอป
รูปที่ 2 ขั้นตอนการประมวลผลวิดเจ็ตแอป

หากวิดเจ็ตต้องมีการกำหนดค่าของผู้ใช้ ให้ใช้กิจกรรมการกำหนดค่าวิดเจ็ตแอป กิจกรรมนี้ช่วยให้ผู้ใช้แก้ไขการตั้งค่าวิดเจ็ตได้ เช่น เขตเวลาสำหรับวิดเจ็ตนาฬิกา

นอกจากนี้ เรายังแนะนำการปรับปรุงต่อไปนี้ด้วย ได้แก่ เลย์เอาต์วิดเจ็ตที่ยืดหยุ่น การปรับปรุงอื่นๆ วิดเจ็ตขั้นสูง วิดเจ็ตคอลเล็กชัน และการสร้างวิดเจ็ต โฮสต์

ประกาศ XML ของ AppWidgetProviderInfo

ออบเจ็กต์ AppWidgetProviderInfo จะกำหนดคุณสมบัติที่จำเป็นของวิดเจ็ต กำหนดออบเจ็กต์ AppWidgetProviderInfo ในไฟล์ทรัพยากร XML โดยใช้องค์ประกอบ <appwidget-provider> เดียว แล้วบันทึกไว้ในโฟลเดอร์ res/xml/ ของโปรเจ็กต์

ตัวอย่างต่อไปนี้แสดงให้เห็นถึงการดำเนินการนี้

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="40dp"
    android:minHeight="40dp"
    android:targetCellWidth="1"
    android:targetCellHeight="1"
    android:maxResizeWidth="250dp"
    android:maxResizeHeight="120dp"
    android:updatePeriodMillis="86400000"
    android:description="@string/example_appwidget_description"
    android:previewLayout="@layout/example_appwidget_preview"
    android:initialLayout="@layout/example_loading_appwidget"
    android:configure="com.example.android.ExampleAppWidgetConfigurationActivity"
    android:resizeMode="horizontal|vertical"
    android:widgetCategory="home_screen"
    android:widgetFeatures="reconfigurable|configuration_optional">
</appwidget-provider>

แอตทริบิวต์การปรับขนาดวิดเจ็ต

หน้าจอหลักเริ่มต้นจะวางวิดเจ็ตในหน้าต่างตามตารางกริดของเซลล์ ที่มีความสูงและความกว้างที่กำหนด หน้าจอหลักส่วนใหญ่จะอนุญาตให้วิดเจ็ตมีขนาดเป็นจำนวนเต็มเท่าของเซลล์กริดเท่านั้น เช่น 2 เซลล์ในแนวนอนและ 3 เซลล์ในแนวตั้ง

แอตทริบิวต์การกำหนดขนาดวิดเจ็ตช่วยให้คุณระบุขนาดเริ่มต้นสำหรับวิดเจ็ตและ ระบุขอบเขตล่างและบนของขนาดวิดเจ็ตได้ ในบริบทนี้ ขนาดเริ่มต้นของวิดเจ็ตคือขนาดที่วิดเจ็ตใช้เมื่อ เพิ่มลงในหน้าจอหลักเป็นครั้งแรก

ตารางต่อไปนี้จะอธิบายแอตทริบิวต์ <appwidget-provider> ที่เกี่ยวข้อง กับการปรับขนาดวิดเจ็ต

แอตทริบิวต์และคำอธิบาย
targetCellWidth และ targetCellHeight (Android 12) minWidth และ minHeight
  • ตั้งแต่ Android 12 เป็นต้นไป แอตทริบิวต์ targetCellWidth และ targetCellHeight จะระบุขนาดเริ่มต้นของวิดเจ็ตในแง่ของตารางกริด ระบบจะไม่สนใจแอตทริบิวต์เหล่านี้ใน Android 11 และต่ำกว่า และอาจไม่สนใจหากหน้าจอหลักไม่ รองรับเลย์เอาต์แบบตารางกริด
  • แอตทริบิวต์ minWidth และ minHeight ระบุขนาดเริ่มต้นของวิดเจ็ต ในหน่วย dp หากค่าความกว้างหรือความสูงขั้นต่ำของวิดเจ็ตไม่ตรงกับขนาดของเซลล์ ระบบจะปัดเศษค่าขึ้นเป็นขนาดเซลล์ที่ใกล้ที่สุด
เราขอแนะนำให้ระบุทั้ง 2 ชุดของ แอตทริบิวต์ ได้แก่ targetCellWidth และ targetCellHeight รวมถึง minWidth และ minHeight เพื่อให้แอปของคุณกลับไปใช้ minWidth และ minHeight ได้หากอุปกรณ์ของผู้ใช้ ไม่รองรับ targetCellWidth และ targetCellHeight หากรองรับ แอตทริบิวต์ targetCellWidth และ targetCellHeight จะมีความสำคัญเหนือกว่าแอตทริบิวต์ minWidth และ minHeight
minResizeWidth และ minResizeHeight ระบุขนาดขั้นต่ำที่แน่นอนของวิดเจ็ต ค่าเหล่านี้ระบุ ขนาดที่วิดเจ็ตอ่านไม่ออกหรือใช้ไม่ได้ การใช้ แอตทริบิวต์เหล่านี้ช่วยให้ผู้ใช้ปรับขนาดวิดเจ็ตให้มีขนาดเล็กกว่า ขนาดวิดเจ็ตเริ่มต้นได้ ระบบจะละเว้นแอตทริบิวต์ minResizeWidth หากมีค่ามากกว่า minWidth หรือหากไม่ได้เปิดใช้การปรับขนาดแนวนอน ดูresizeMode ในทำนองเดียวกัน ระบบจะละเว้นแอตทริบิวต์ minResizeHeight หากมีค่ามากกว่า minHeight หรือหากไม่ได้เปิดใช้การปรับขนาดแนวตั้ง
maxResizeWidth และ maxResizeHeight ระบุขนาดสูงสุดที่แนะนำของวิดเจ็ต หากค่าไม่ใช่ ผลคูณของขนาดเซลล์กริด ระบบจะปัดเศษขึ้นเป็นขนาดเซลล์ที่ใกล้เคียงที่สุด ระบบจะละเว้นแอตทริบิวต์ maxResizeWidth หากมีขนาดเล็กกว่า minWidth หรือหากไม่ได้เปิดใช้การปรับขนาดแนวนอน ดู resizeMode ในทำนองเดียวกัน ระบบจะละเว้นแอตทริบิวต์ maxResizeHeight หากมีค่ามากกว่า minHeight หรือหากไม่ได้เปิดใช้การปรับขนาดแนวตั้ง เปิดตัวใน Android 12
resizeMode ระบุกฎที่ใช้ในการปรับขนาดวิดเจ็ต คุณใช้แอตทริบิวต์นี้ เพื่อทำให้วิดเจ็ตหน้าจอหลักปรับขนาดได้ในแนวนอน แนวตั้ง หรือทั้ง 2 แกน ผู้ใช้แตะวิดเจ็ตค้างไว้เพื่อแสดงแฮนเดิลปรับขนาด จากนั้นลากแฮนเดิลแนวนอนหรือแนวตั้งเพื่อเปลี่ยนขนาดใน ตารางกริดของเลย์เอาต์ ค่าสำหรับแอตทริบิวต์ resizeMode ได้แก่ horizontal, vertical และ none หากต้องการ ประกาศวิดเจ็ตว่าปรับขนาดได้ทั้งแนวนอนและแนวตั้ง ให้ใช้ horizontal|vertical

ตัวอย่าง

โปรดดูข้อมูลจำเพาะต่อไปนี้เพื่อดูว่าแอตทริบิวต์ในตารางก่อนหน้าส่งผลต่อการปรับขนาดวิดเจ็ตอย่างไร

  • เซลล์ตารางกริดมีความกว้าง 30 dp และสูง 50 dp
  • เราได้ระบุข้อกำหนดแอตทริบิวต์ต่อไปนี้
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:minWidth="80dp"
    android:minHeight="80dp"
    android:targetCellWidth="2"
    android:targetCellHeight="2"
    android:minResizeWidth="40dp"
    android:minResizeHeight="40dp"
    android:maxResizeWidth="120dp"
    android:maxResizeHeight="120dp"
    android:resizeMode="horizontal|vertical" />

ตั้งแต่ Android 12 เป็นต้นไป

ใช้แอตทริบิวต์ targetCellWidth และ targetCellHeight เป็นขนาดเริ่มต้นของวิดเจ็ต

วิดเจ็ตมีขนาด 2x2 โดยค่าเริ่มต้น คุณปรับขนาดวิดเจ็ตให้เล็กลงได้ถึง 2x1 หรือใหญ่ขึ้นได้ถึง 4x3

Android 11 และต่ำกว่า

ใช้แอตทริบิวต์ minWidth และ minHeight เพื่อคำนวณขนาดเริ่มต้นของ วิดเจ็ต

ความกว้างเริ่มต้น = Math.ceil(80 / 30) = 3

ความสูงเริ่มต้น = Math.ceil(80 / 50) = 2

ขนาดเริ่มต้นของวิดเจ็ตคือ 3x2 คุณปรับขนาดวิดเจ็ตให้เล็กลงได้ถึง 2x1 หรือ ขยายให้เต็มหน้าจอได้

แอตทริบิวต์เพิ่มเติมของวิดเจ็ต

ตารางต่อไปนี้อธิบายแอตทริบิวต์ <appwidget-provider> ที่เกี่ยวข้อง กับคุณภาพอื่นๆ นอกเหนือจากการปรับขนาดวิดเจ็ต

แอตทริบิวต์และคำอธิบาย
updatePeriodMillis กำหนดความถี่ที่เฟรมเวิร์กวิดเจ็ตขออัปเดตจาก AppWidgetProvider โดยการเรียกใช้เมธอดเรียกกลับ onUpdate() การอัปเดตจริงอาจไม่เกิดขึ้นตรงเวลาตามค่านี้เสมอไป และเราขอแนะนำให้อัปเดตให้น้อยที่สุดเท่าที่จะเป็นไปได้ โดยไม่เกิน 1 ครั้งต่อชั่วโมง เพื่อประหยัดแบตเตอรี่ ดูรายการข้อควรพิจารณาทั้งหมดในการเลือกช่วงเวลาอัปเดตที่เหมาะสมได้ที่ การเพิ่มประสิทธิภาพสำหรับการอัปเดตเนื้อหาวิดเจ็ต
initialLayout ชี้ไปยังทรัพยากรเลย์เอาต์ที่กำหนดเลย์เอาต์ของวิดเจ็ต
configure กำหนดกิจกรรมที่จะเปิดขึ้นเมื่อผู้ใช้เพิ่มวิดเจ็ต เพื่อให้ผู้ใช้กำหนดค่าพร็อพเพอร์ตี้ของวิดเจ็ตได้ ดู อนุญาตให้ผู้ใช้กำหนดค่าวิดเจ็ต ตั้งแต่ Android 12 เป็นต้นไป แอปจะข้ามการกำหนดค่าเริ่มต้นได้ ดูรายละเอียดได้ที่ใช้การกำหนดค่าเริ่มต้นของวิดเจ็ต
description ระบุคำอธิบายสำหรับเครื่องมือเลือกวิดเจ็ตที่จะแสดงสำหรับวิดเจ็ต ของคุณ เปิดตัวใน Android 12
previewLayout (Android 12) และ previewImage (Android 11 และต่ำกว่า)
  • ตั้งแต่ Android 12 เป็นต้นไป แอตทริบิวต์ previewLayout จะระบุตัวอย่างที่ปรับขนาดได้ ซึ่ง คุณระบุเป็นเลย์เอาต์ XML ที่ตั้งค่าเป็นขนาดเริ่มต้นของวิดเจ็ต ในอุดมคติ XML เลย์เอาต์ที่ระบุเป็นแอตทริบิวต์นี้คือ XML เลย์เอาต์เดียวกันกับ วิดเจ็ตจริงที่มีค่าเริ่มต้นที่สมจริง
  • ใน Android 11 หรือต่ำกว่า แอตทริบิวต์ previewImage จะระบุตัวอย่างลักษณะของวิดเจ็ตหลังจากที่ กำหนดค่าแล้ว ซึ่งผู้ใช้จะเห็นเมื่อเลือกวิดเจ็ตแอป หากไม่ได้ระบุไว้ ผู้ใช้จะเห็นไอคอน Launcher ของแอปแทน ฟิลด์นี้สอดคล้องกับแอตทริบิวต์ android:previewImage ในองค์ประกอบ <receiver> ในไฟล์ AndroidManifest.xml
หมายเหตุ: เราขอแนะนำให้ระบุทั้งแอตทริบิวต์ previewImage และ previewLayout เพื่อให้แอปสามารถกลับไปใช้ previewImage ได้ หากอุปกรณ์ของผู้ใช้ไม่รองรับ previewLayout โปรดดูรายละเอียดเพิ่มเติมที่ ความเข้ากันได้แบบย้อนหลังกับ ตัวอย่างวิดเจ็ตที่ปรับขนาดได้
autoAdvanceViewId ระบุรหัสมุมมองของมุมมองย่อยของวิดเจ็ตที่โฮสต์ของวิดเจ็ตเลื่อนไปข้างหน้าโดยอัตโนมัติ
widgetCategory ประกาศว่าวิดเจ็ตแสดงในหน้าจอหลัก (home_screen), หน้าจอล็อก (keyguard) หรือทั้ง 2 หน้าจอได้หรือไม่ สำหรับ Android 5.0 ขึ้นไป จะใช้ได้เฉพาะ home_screen
widgetFeatures ประกาศฟีเจอร์ที่วิดเจ็ตนี้รองรับ เช่น หากต้องการให้ วิดเจ็ตใช้การกำหนดค่าเริ่มต้นเมื่อผู้ใช้เพิ่มวิดเจ็ต ให้ระบุทั้ง configuration_optional และ reconfigurable ซึ่งจะข้ามการเปิดใช้งานการกำหนดค่าหลังจากที่ผู้ใช้ เพิ่มวิดเจ็ต ผู้ใช้จะยังคง กำหนดค่าเครื่องมือใหม่ ได้ในภายหลัง

ใช้คลาส AppWidgetProvider เพื่อจัดการการออกอากาศวิดเจ็ต

คลาส AppWidgetProvider จะจัดการการออกอากาศวิดเจ็ตและอัปเดตวิดเจ็ต เพื่อตอบสนองต่อเหตุการณ์วงจรของวิดเจ็ต ส่วนต่อไปนี้อธิบายวิธีประกาศ AppWidgetProvider ในไฟล์ Manifest แล้วนำไปใช้

ประกาศวิดเจ็ตในไฟล์ Manifest

ก่อนอื่น ให้ประกาศคลาส AppWidgetProvider ในไฟล์ AndroidManifest.xml ของแอป ดังที่แสดงในตัวอย่างต่อไปนี้

<receiver android:name="ExampleAppWidgetProvider"
                 android:exported="false">
    <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>

องค์ประกอบ <receiver> ต้องมีแอตทริบิวต์ android:name ซึ่งระบุ AppWidgetProvider ที่วิดเจ็ตใช้ ห้ามส่งออกคอมโพเนนต์ เว้นแต่จะต้องมีกระบวนการแยกต่างหากเพื่อออกอากาศไปยัง AppWidgetProvider ซึ่งโดยปกติแล้ว จะไม่ใช่กรณีนี้

องค์ประกอบ <intent-filter> ต้องมีองค์ประกอบ <action> ที่มีแอตทริบิวต์ android:name แอตทริบิวต์นี้ระบุว่า AppWidgetProvider ยอมรับACTION_APPWIDGET_UPDATE การออกอากาศ นี่คือการออกอากาศเพียงรายการเดียวที่คุณต้องประกาศอย่างชัดเจน โดย AppWidgetManager จะส่งการออกอากาศวิดเจ็ตอื่นๆ ทั้งหมดไปยัง AppWidgetProvider โดยอัตโนมัติ ตามความจำเป็น

องค์ประกอบ <meta-data> จะระบุAppWidgetProviderInfoทรัพยากรและต้องมีแอตทริบิวต์ต่อไปนี้

  • android:name: ระบุชื่อข้อมูลเมตา ใช้ android.appwidget.provider เพื่อระบุข้อมูลเป็นตัวอธิบาย AppWidgetProviderInfo
  • android:resource: ระบุตำแหน่งของทรัพยากร AppWidgetProviderInfo

ใช้คลาส AppWidgetProvider

คลาส AppWidgetProvider จะขยาย BroadcastReceiver เป็นคลาสอำนวยความสะดวกเพื่อจัดการการออกอากาศวิดเจ็ต โดยจะรับเฉพาะการออกอากาศเหตุการณ์ที่เกี่ยวข้องกับวิดเจ็ต เช่น เมื่อมีการอัปเดต ลบ เปิดใช้ และปิดใช้วิดเจ็ต เมื่อเกิดเหตุการณ์การออกอากาศเหล่านี้ ระบบจะเรียกใช้AppWidgetProviderวิธีการต่อไปนี้

onUpdate()
ระบบจะเรียกใช้ฟังก์ชันนี้เพื่ออัปเดตวิดเจ็ตตามช่วงเวลาที่กำหนดโดยแอตทริบิวต์ updatePeriodMillis ใน AppWidgetProviderInfo ดูข้อมูลเพิ่มเติมได้ที่ตาราง ที่อธิบายแอตทริบิวต์วิดเจ็ตเพิ่มเติมในหน้านี้
เมธอดนี้จะเรียกใช้เมื่อผู้ใช้เพิ่มวิดเจ็ตด้วย จึงทำการตั้งค่าที่จำเป็น เช่น การกำหนดตัวแฮนเดิลเหตุการณ์สำหรับออบเจ็กต์ View หรือการเริ่มงานเพื่อโหลดข้อมูลที่จะแสดงในวิดเจ็ต อย่างไรก็ตาม หากคุณประกาศกิจกรรมการกำหนดค่าโดยไม่มี แฟล็ก configuration_optional ระบบจะไม่เรียกใช้วิธีนี้เมื่อผู้ใช้ เพิ่มวิดเจ็ต แต่จะเรียกใช้สำหรับการอัปเดตในภายหลัง กิจกรรมการกำหนดค่ามี หน้าที่รับผิดชอบในการอัปเดตครั้งแรกเมื่อ การกำหนดค่าเสร็จสมบูรณ์ โปรดดูข้อมูลเพิ่มเติมที่หัวข้อเปิดใช้ให้ผู้ใช้กำหนดค่าวิดเจ็ตแอป
การเรียกกลับที่สำคัญที่สุดคือ onUpdate() ดูข้อมูลเพิ่มเติมได้ที่จัดการเหตุการณ์ด้วยคลาส onUpdate()ในหน้านี้
onAppWidgetOptionsChanged()

เรียกใช้เมื่อวางวิดเจ็ตเป็นครั้งแรกและทุกครั้งที่ มีการปรับขนาดวิดเจ็ต ใช้การเรียกกลับนี้เพื่อแสดงหรือซ่อนเนื้อหาตามช่วงขนาดของวิดเจ็ต รับช่วงขนาด และตั้งแต่ Android 12 เป็นต้นไป รายการขนาดที่เป็นไปได้ที่อินสแตนซ์วิดเจ็ตใช้ได้โดยการเรียก getAppWidgetOptions() ซึ่งจะแสดงผล Bundle ที่มีข้อมูลต่อไปนี้

  • OPTION_APPWIDGET_MIN_WIDTH: มีขอบเขตล่างของความกว้างในหน่วย dp ของอินสแตนซ์วิดเจ็ต
  • OPTION_APPWIDGET_MIN_HEIGHT: มีขอบเขตล่างของความสูงในหน่วย dp ของอินสแตนซ์วิดเจ็ต
  • OPTION_APPWIDGET_MAX_WIDTH: มีขอบเขตบนของความกว้างในหน่วย dp ของอินสแตนซ์วิดเจ็ต
  • OPTION_APPWIDGET_MAX_HEIGHT: มีขอบเขตบนของความสูงในหน่วย dp ของอินสแตนซ์วิดเจ็ต
  • OPTION_APPWIDGET_SIZES: มีรายการขนาดที่เป็นไปได้ (List<SizeF>) ในหน่วย dp ที่อินสแตนซ์วิดเจ็ต ใช้ได้ เปิดตัวใน Android 12
onDeleted(Context, int[])

ระบบจะเรียกใช้ฟังก์ชันนี้ทุกครั้งที่ลบวิดเจ็ตออกจากโฮสต์วิดเจ็ต

onEnabled(Context)

ระบบจะเรียกใช้ฟังก์ชันนี้เมื่อสร้างอินสแตนซ์ของวิดเจ็ตเป็นครั้งแรก เช่น หากผู้ใช้เพิ่มวิดเจ็ตของคุณ 2 อินสแตนซ์ ระบบจะเรียกใช้ฟังก์ชันนี้เฉพาะครั้งแรกเท่านั้น หากคุณต้องการเปิดฐานข้อมูลใหม่หรือทำการตั้งค่าอื่นที่ จำเป็นต้องทำเพียงครั้งเดียวสำหรับอินสแตนซ์วิดเจ็ตทั้งหมด นี่คือตำแหน่งที่เหมาะสม ในการดำเนินการ

onDisabled(Context)

ระบบจะเรียกใช้ฟังก์ชันนี้เมื่อลบอินสแตนซ์สุดท้ายของวิดเจ็ตออกจาก โฮสต์วิดเจ็ต ส่วนนี้คือที่ที่คุณจะล้างงานที่ทำใน onEnabled(Context) เช่น การลบฐานข้อมูลชั่วคราว

onReceive(Context, Intent)

ระบบจะเรียกใช้เมธอดนี้สำหรับการออกอากาศทุกครั้งและก่อนเมธอดเรียกกลับก่อนหน้าแต่ละรายการ โดยปกติแล้วคุณไม่จำเป็นต้องใช้เมธอดนี้ เนื่องจากAppWidgetProviderการใช้งานเริ่มต้นจะกรองการออกอากาศของวิดเจ็ตทั้งหมดและเรียกใช้เมธอดก่อนหน้าตามความเหมาะสม

คุณต้องประกาศการใช้งานคลาส AppWidgetProvider เป็น Broadcast Receiver โดยใช้องค์ประกอบ <receiver> ใน AndroidManifest ดูข้อมูลเพิ่มเติมได้ที่ประกาศวิดเจ็ตในไฟล์ Manifest ในหน้านี้

จัดการเหตุการณ์ด้วยคลาส onUpdate()

AppWidgetProvider Callback ที่สำคัญที่สุดคือ onUpdate() เนื่องจากจะมีการเรียกใช้เมื่อเพิ่มวิดเจ็ตแต่ละรายการลงในโฮสต์ เว้นแต่คุณจะใช้กิจกรรมการกำหนดค่าที่ไม่มีแฟล็ก configuration_optional หากวิดเจ็ตยอมรับเหตุการณ์การโต้ตอบของผู้ใช้ ให้ลงทะเบียนตัวแฮนเดิลเหตุการณ์ใน Callback นี้ หากวิดเจ็ตไม่ได้สร้างไฟล์หรือฐานข้อมูลชั่วคราว หรือทำงานอื่นๆ ที่ต้องมีการล้างข้อมูล onUpdate() อาจเป็นเมธอดการเรียกกลับเพียงเมธอดเดียวที่คุณ ต้องกำหนด

ตัวอย่างเช่น หากต้องการวิดเจ็ตที่มีปุ่มซึ่งเปิดใช้กิจกรรมเมื่อมีการแตะ คุณสามารถใช้การติดตั้งใช้งาน AppWidgetProvider ต่อไปนี้

Kotlin

class ExampleAppWidgetProvider : AppWidgetProvider() {

    override fun onUpdate(
            context: Context,
            appWidgetManager: AppWidgetManager,
            appWidgetIds: IntArray
    ) {
        // Perform this loop procedure for each widget that belongs to this
        // provider.
        appWidgetIds.forEach { appWidgetId ->
            // Create an Intent to launch ExampleActivity.
            val pendingIntent: PendingIntent = PendingIntent.getActivity(
                    /* context = */ context,
                    /* requestCode = */  0,
                    /* intent = */ Intent(context, ExampleActivity::class.java),
                    /* flags = */ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
            )

            // Get the layout for the widget and attach an onClick 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
            // widget.
            appWidgetManager.updateAppWidget(appWidgetId, views)
        }
    }
}

Java

public class ExampleAppWidgetProvider extends AppWidgetProvider {

    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        // Perform this loop procedure for each widget that belongs to this
        // provider.
        for (int i=0; i < appWidgetIds.length; i++) {
            int appWidgetId = appWidgetIds[i];
            // Create an Intent to launch ExampleActivity
            Intent intent = new Intent(context, ExampleActivity.class);
            PendingIntent pendingIntent = PendingIntent.getActivity(
                /* context = */ context,
                /* requestCode = */ 0,
                /* intent = */ intent,
                /* flags = */ PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
            );

            // Get the layout for the widget and attach an onClick listener to
            // the button.
            RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.example_appwidget_layout);
            views.setOnClickPendingIntent(R.id.button, pendingIntent);

            // Tell the AppWidgetManager to perform an update on the current app
            // widget.
            appWidgetManager.updateAppWidget(appWidgetId, views);
        }
    }
}

AppWidgetProvider นี้จะกำหนดเฉพาะเมธอด onUpdate() โดยใช้เมธอดนี้เพื่อ สร้าง PendingIntent ที่เปิด Activity และแนบไปกับปุ่มของวิดเจ็ต โดยใช้ setOnClickPendingIntent(int, PendingIntent) โดยมีลูปที่วนซ้ำในแต่ละรายการ ใน appWidgetIds ซึ่งเป็นอาร์เรย์ของรหัสที่ระบุวิดเจ็ตแต่ละรายการที่สร้างโดย ผู้ให้บริการรายนี้ หากผู้ใช้สร้างวิดเจ็ตมากกว่า 1 รายการ วิดเจ็ตทั้งหมดจะอัปเดตพร้อมกัน อย่างไรก็ตาม ระบบจะจัดการupdatePeriodMillisกำหนดเวลา เพียงรายการเดียวสำหรับวิดเจ็ตทุกอินสแตนซ์ เช่น หากกำหนดตารางเวลาการอัปเดต ให้ทุก 2 ชั่วโมง และเพิ่มวิดเจ็ตอินสแตนซ์ที่ 2 หลังจากอินสแตนซ์แรก 1 ชั่วโมง ทั้ง 2 อินสแตนซ์จะได้รับการอัปเดตตามระยะเวลาที่กำหนดโดย อินสแตนซ์แรก และระบบจะไม่สนใจระยะเวลาการอัปเดตที่ 2 ทั้ง 2 อย่างจะอัปเดตทุก 2 ชั่วโมง ไม่ใช่ทุกชั่วโมง

ดูรายละเอียดเพิ่มเติมได้ที่คลาสตัวอย่าง ExampleAppWidgetProvider.java

รับ Intent การออกอากาศของวิดเจ็ต

AppWidgetProvider เป็นคลาสช่วยอำนวยความสะดวก หากต้องการรับการออกอากาศวิดเจ็ตโดยตรง คุณสามารถใช้ BroadcastReceiver ของคุณเองหรือลบล้าง การเรียกกลับ onReceive(Context,Intent) Intent ที่คุณต้องสนใจมีดังนี้

สร้างเลย์เอาต์วิดเจ็ต

คุณต้องกำหนดเลย์เอาต์เริ่มต้นสำหรับวิดเจ็ตใน XML และบันทึกไว้ในไดเรกทอรี res/layout/ ของโปรเจ็กต์ ดูรายละเอียดได้ในหลักเกณฑ์ การออกแบบ

การสร้างเลย์เอาต์วิดเจ็ตนั้นตรงไปตรงมาหากคุณคุ้นเคยกับเลย์เอาต์ อย่างไรก็ตาม โปรดทราบว่าเลย์เอาต์วิดเจ็ต อิงตาม RemoteViews ซึ่งไม่รองรับเลย์เอาต์หรือวิดเจ็ตมุมมองทุกประเภท คุณไม่สามารถใช้มุมมองที่กำหนดเองหรือคลาสย่อยของมุมมองที่ RemoteViews รองรับ

RemoteViews ยังรองรับ ViewStub ซึ่งเป็น View ที่มองไม่เห็นและมีขนาดเป็น 0 ซึ่งคุณใช้เพื่อขยายเลย์เอาต์ ทรัพยากรแบบเลซีโหลดได้ในรันไทม์

รองรับลักษณะการทำงานแบบมีสถานะ

Android 12 เพิ่มการรองรับลักษณะการทำงานแบบมีสถานะโดยใช้คอมโพเนนต์ที่มีอยู่ต่อไปนี้

วิดเจ็ตยังคงไม่มีสถานะ แอปต้องจัดเก็บสถานะและลงทะเบียนเพื่อรับ เหตุการณ์การเปลี่ยนแปลงสถานะ

ตัวอย่างวิดเจ็ตรายการช็อปปิ้งที่แสดงลักษณะการทำงานแบบมีสถานะ
รูปที่ 3 ตัวอย่างลักษณะการทำงานแบบมีสถานะ

ตัวอย่างโค้ดต่อไปนี้แสดงวิธีติดตั้งใช้งานคอมโพเนนต์เหล่านี้

Kotlin

// Check the view.
remoteView.setCompoundButtonChecked(R.id.my_checkbox, true)

// Check a radio group.
remoteView.setRadioGroupChecked(R.id.my_radio_group, R.id.radio_button_2)

// Listen for check changes. The intent has an extra with the key
// EXTRA_CHECKED that specifies the current checked state of the view.
remoteView.setOnCheckedChangeResponse(
        R.id.my_checkbox,
        RemoteViews.RemoteResponse.fromPendingIntent(onCheckedChangePendingIntent)
)

Java

// Check the view.
remoteView.setCompoundButtonChecked(R.id.my_checkbox, true);

// Check a radio group.
remoteView.setRadioGroupChecked(R.id.my_radio_group, R.id.radio_button_2);

// Listen for check changes. The intent has an extra with the key
// EXTRA_CHECKED that specifies the current checked state of the view.
remoteView.setOnCheckedChangeResponse(
    R.id.my_checkbox,
    RemoteViews.RemoteResponse.fromPendingIntent(onCheckedChangePendingIntent));

ระบุเลย์เอาต์ 2 รายการ ได้แก่ รายการหนึ่งที่กำหนดเป้าหมายเป็นอุปกรณ์ที่ใช้ Android 12 ขึ้นไปใน res/layout-v31 และอีกรายการหนึ่งที่กำหนดเป้าหมายเป็น Android 11 หรือต่ำกว่าในโฟลเดอร์ res/layout เริ่มต้น

ใช้มุมโค้งมน

Android 12 เปิดตัวพารามิเตอร์ระบบต่อไปนี้เพื่อตั้งค่า รัศมีของมุมโค้งของวิดเจ็ต

  • system_app_widget_background_radius: รัศมีมุมของพื้นหลังวิดเจ็ต ซึ่งไม่เกิน 28 dp

  • รัศมีด้านใน ซึ่งคำนวณได้จากรัศมีด้านนอกและการเว้นวรรค ดูข้อมูลโค้ดต่อไปนี้

    /**
     * Applies corner radius for views that are visually positioned [widgetPadding]dp inside of the
     * widget background.
     */
    @Composable
    fun GlanceModifier.appWidgetInnerCornerRadius(widgetPadding: Dp): GlanceModifier {
    
        if (Build.VERSION.SDK_INT < 31) {
            return this
        }
    
        val resources = LocalContext.current.resources
        // get dimension in float (without rounding).
        val px = resources.getDimension(android.R.dimen.system_app_widget_background_radius)
        val widgetBackgroundRadiusDpValue = px / resources.displayMetrics.density
        if (widgetBackgroundRadiusDpValue < widgetPadding.value) {
            return this
        }
        return this.cornerRadius(Dp(widgetBackgroundRadiusDpValue - widgetPadding.value))
    }

หากต้องการคำนวณรัศมีที่เหมาะสมสำหรับเนื้อหาด้านในของวิดเจ็ต ให้ใช้สูตรต่อไปนี้ systemRadiusValue - widgetPadding

วิดเจ็ตที่ตัดเนื้อหาให้เป็นรูปร่างที่ไม่ใช่สี่เหลี่ยมผืนผ้าควรใช้ @android:id/background เป็นรหัสมุมมองของมุมมองพื้นหลังที่มี android:clipToOutline ตั้งค่าเป็น true

ข้อควรพิจารณาที่สำคัญสำหรับมุมโค้ง

  • ตัวเรียกใช้งานของบุคคลที่สามและผู้ผลิตอุปกรณ์สามารถลบล้างพารามิเตอร์ system_app_widget_background_radius ให้มีขนาดเล็กกว่า 28 dp ได้
  • หากวิดเจ็ตไม่ได้ใช้ @android:id/background หรือกำหนดพื้นหลัง ที่ตัดเนื้อหาตามโครงร่างโดยตั้งค่า android:clipToOutline เป็น true ตัวเรียกใช้จะระบุพื้นหลังโดยอัตโนมัติและตัด วิดเจ็ตโดยใช้สี่เหลี่ยมผืนผ้าที่มีมุมโค้งซึ่งตั้งค่าเป็นรัศมีของระบบ

  • รูปร่างที่ไม่ใช่สี่เหลี่ยมผืนผ้าต้องอยู่ในคอนเทนเนอร์การปรับขนาดสี่เหลี่ยมผืนผ้าโค้งมน เพื่อไม่ให้ถูกตัด

  • ตั้งแต่ Android 16 เป็นต้นไป ค่าระบบ AOSP สำหรับ system_app_widget_background_radius คือ 24dp โปรแกรมเรียกใช้และผู้ผลิตอุปกรณ์อาจตัดวิดเจ็ตให้พอดีกับsystem_app_widget_background_radius

  • เนื้อหาด้านในของวิดเจ็ตต้องมีระยะขอบเพียงพอที่จะรองรับsystem_app_widget_background_radiusค่ารัศมีสูงสุด 28dp เพื่อหลีกเลี่ยงไม่ให้มุมโค้งตัดเนื้อหา

หากต้องการให้วิดเจ็ตใช้งานร่วมกับ Android เวอร์ชันก่อนหน้าได้ เราขอแนะนำให้ กำหนดแอตทริบิวต์ที่กำหนดเองและใช้ธีมที่กำหนดเองเพื่อลบล้างแอตทริบิวต์เหล่านั้นสำหรับ Android 12 ดังที่แสดงในไฟล์ XML ตัวอย่างต่อไปนี้

/values/attrs.xml

<resources>
  <attr name="backgroundRadius" format="dimension" />
</resources>

/values/styles.xml

<resources>
  <style name="MyWidgetTheme">
    <item name="backgroundRadius">@dimen/my_background_radius_dimen</item>
  </style>
</resources>

/values-31/styles.xml

<resources>
  <style name="MyWidgetTheme" parent="@android:style/Theme.DeviceDefault.DayNight">
    <item name="backgroundRadius">@android:dimen/system_app_widget_background_radius</item>
  </style>
</resources>

/drawable/my_widget_background.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android"
  android:shape="rectangle">
  <corners android:radius="?attr/backgroundRadius" />
  ...
</shape>

/layout/my_widget_layout.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  ...
  android:background="@drawable/my_widget_background" />