สร้างวิดเจ็ตแอปด้วย "ข้อมูลโดยย่อ"

ส่วนต่อไปนี้จะอธิบายวิธีสร้าง App Widget พื้นฐานด้วย Glance

ประกาศ AppWidget ในไฟล์ Manifest

หลังจากทำตามขั้นตอนการตั้งค่าแล้ว ให้ประกาศ AppWidget และ ข้อมูลเมตาในแอป

  1. ขยายตัวรับ AppWidget จาก GlanceAppWidgetReceiver ดังนี้

    class MyAppWidgetReceiver : GlanceAppWidgetReceiver() {
        override val glanceAppWidget: GlanceAppWidget = TODO("Create GlanceAppWidget")
    }

  2. ลงทะเบียนผู้ให้บริการ App Widget ในไฟล์ AndroidManifest.xml และไฟล์ข้อมูลเมตาที่เชื่อมโยง

        <receiver android:name=".glance.MyReceiver"
        android:exported="true">
        <intent-filter>
            <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
        </intent-filter>
        <meta-data
            android:name="android.appwidget.provider"
            android:resource="@xml/my_app_widget_info" />
    </receiver>
    

เพิ่มข้อมูลเมตา AppWidgetProviderInfo

จากนั้นทำตามคู่มือ สร้างวิดเจ็ต เพื่อสร้างและกำหนดข้อมูล App Widget ในไฟล์ @xml/my_app_widget_info

ความแตกต่างเพียงอย่างเดียวของ Glance คือไม่มี XML initialLayout แต่คุณต้องกำหนด XML นี้ โดยสามารถใช้เลย์เอาต์การโหลดที่กำหนดไว้ล่วงหน้าซึ่งมีให้ในไลบรารีได้ดังนี้

<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
    android:initialLayout="@layout/glance_default_loading_layout">
</appwidget-provider>

ประกาศ XML ของ AppWidgetProviderInfo

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

<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/glance_default_loading_layout"
    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 กำหนดความถี่ที่เฟรมเวิร์กวิดเจ็ตจะขอการอัปเดตจาก GlanceAppWidgetReceiver โดยการเรียกใช้เมธอด Callback onUpdate() เราขอแนะนำให้อัปเดตให้น้อยที่สุดเท่าที่จะทำได้ โดยไม่เกิน 1 ครั้งต่อชั่วโมง เพื่อประหยัดแบตเตอรี่ ดูรายละเอียดได้ที่ส่วนเวลาที่ควรจะอัปเดตวิดเจ็ตในการจัดการสถานะ Glance
initialLayout ชี้ไปยังทรัพยากรเลย์เอาต์ที่กำหนดเลย์เอาต์การโหลดของวิดเจ็ตก่อนที่องค์ประกอบ UI ของ Glance จะแสดงผล คุณสามารถใช้เลย์เอาต์การโหลดที่กำหนดไว้ล่วงหน้าซึ่งมีให้ในไลบรารีได้ดังนี้ @layout/glance_default_loading_layout
configure กำหนดกิจกรรมการกำหนดค่าที่จะเปิดตัวเมื่อผู้ใช้เพิ่มวิดเจ็ต ดูคู่มือใช้กิจกรรมการกำหนดค่า
description ระบุคำอธิบายสำหรับเครื่องมือเลือกวิดเจ็ตที่จะแสดงสำหรับวิดเจ็ต เปิดตัวใน Android 12
previewLayout (Android 12) และ previewImage (Android 11 และต่ำกว่า)
  • ตั้งแต่ Android 12 เป็นต้นมา แอตทริบิวต์ previewLayout จะระบุการแสดงตัวอย่างที่ปรับขนาดได้ ซึ่ง คุณจะระบุเป็นชุดเลย์เอาต์ XML ที่ตั้งค่าเป็นขนาดเริ่มต้นของวิดเจ็ต โดยควรชี้ไปยัง การแมป XML แบบคงที่ที่ตรงกับเลย์เอาต์การออกแบบ
  • ใน Android 11 หรือต่ำกว่า previewImage แอตทริบิวต์จะระบุภาพหน้าจอแบบคงที่ที่วาดได้ของลักษณะวิดเจ็ต ซึ่งจะปรากฏในเครื่องมือเลือกวิดเจ็ต
เราขอแนะนำให้ระบุทั้ง 2 อย่างเพื่อให้แอปกลับไปใช้แพลตฟอร์มเก่าได้อย่างราบรื่น สำหรับแพลตฟอร์มใหม่กว่า (Android 15 ขึ้นไป) คุณสามารถกำหนดการแสดงตัวอย่างที่สร้างขึ้นแบบสดใน Kotlin ได้โดยใช้ `GlanceAppWidget.providePreview` ดูคู่มือการแสดงตัวอย่างที่สร้างขึ้น
autoAdvanceViewId ระบุรหัสมุมมองของมุมมองย่อยของวิดเจ็ตที่โฮสต์ของวิดเจ็ตจะเลื่อนไปข้างหน้าโดยอัตโนมัติ
widgetCategory ประกาศว่าวิดเจ็ตจะแสดงในหน้าจอหลัก (home_screen), หน้าจอล็อก (keyguard) หรือทั้ง 2 อย่าง สำหรับ Android 5.0 ขึ้นไป จะใช้ได้เฉพาะ home_screen
widgetFeatures ประกาศฟีเจอร์ที่วิดเจ็ตรองรับ เช่น หากการกำหนดค่าวิดเจ็ตเป็นแบบไม่บังคับ ให้ระบุทั้ง configuration_optional และ reconfigurable

กำหนด GlanceAppWidget

  1. สร้างคลาสใหม่ที่ขยายจาก GlanceAppWidget และลบล้าง เมธอด provideGlance ซึ่งเป็นเมธอดที่คุณสามารถโหลดข้อมูลที่จำเป็นในการแสดงผลวิดเจ็ตได้

    class MyAppWidget : GlanceAppWidget() {
    
        override suspend fun provideGlance(context: Context, id: GlanceId) {
    
            // In this method, load data needed to render the AppWidget.
            // Use `withContext` to switch to another thread for long running
            // operations.
    
            provideContent {
                // create your AppWidget here
                Text("Hello World")
            }
        }
    }

  2. สร้างอินสแตนซ์ใน glanceAppWidget ใน GlanceAppWidgetReceiver ดังนี้

    class MyAppWidgetReceiver : GlanceAppWidgetReceiver() {
    
        // Let MyAppWidgetReceiver know which GlanceAppWidget to use
        override val glanceAppWidget: GlanceAppWidget = MyAppWidget()
    }

ตอนนี้คุณได้กำหนดค่า AppWidget โดยใช้ Glance แล้ว

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

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

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

ประกาศคลาสย่อย GlanceAppWidgetReceiver เป็นตัวรับการออกอากาศในไฟล์ AndroidManifest.xml ดังนี้

<receiver android:name="MyReceiver"
          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/my_app_widget_info" />
</receiver>

องค์ประกอบ <receiver> ต้องมีแอตทริบิวต์ android:name ซึ่งระบุ คลาสตัวรับ ตัวรับต้องยอมรับการดำเนินการออกอากาศ ACTION_APPWIDGET_UPDATE ภายใน <intent-filter>

องค์ประกอบ <meta-data> ต้องระบุชื่อเป็น android.appwidget.provider และแอตทริบิวต์ android:resource ต้องชี้ไปยัง ทรัพยากรข้อมูลเมตา XML ของ AppWidgetProviderInfo (@xml/my_app_widget_info)

ใช้คลาส GlanceAppWidgetReceiver

ใน Glance คุณจะขยาย GlanceAppWidgetReceiver แทนที่จะขยาย AppWidgetProvider โดยตรง โดยใช้คลาสนี้ด้วยการลิงก์ตัวรับกับอินสแตนซ์ GlanceAppWidget การเรียกกลับหลักที่มีใน GlanceAppWidgetReceiver จะทำงานดังนี้

  • onUpdate(): Glance จะลบล้างเมธอดนี้โดยอัตโนมัติเพื่อดำเนินการอัปเดตองค์ประกอบ หากคุณลบล้าง onUpdate ด้วยตนเอง คุณ ต้องเรียกใช้ super.onUpdate เพื่อให้ Glance เปิดใช้เทรดองค์ประกอบได้สำเร็จ
  • onAppWidgetOptionsChanged(): เรียกใช้เมื่อวางหรือปรับขนาดวิดเจ็ตเป็นครั้งแรก Glance จะอ่านรายการชุดตัวเลือกเบื้องหลังเพื่อให้เลย์เอาต์ปรับเปลี่ยนได้อย่างราบรื่นตามขนาดรันไทม์
  • onDeleted(Context, IntArray): เรียกใช้เมื่อผู้ใช้ลบอินสแตนซ์วิดเจ็ตที่เฉพาะเจาะจง
  • onEnabled(Context): ทริกเกอร์เมื่อสร้างอินสแตนซ์แรกของวิดเจ็ตได้สำเร็จ เหมาะสำหรับการเรียกใช้การย้ายข้อมูลส่วนกลาง
  • onDisabled(Context): เรียกใช้เมื่อนำอินสแตนซ์สุดท้ายที่ใช้งานอยู่ของผู้ให้บริการออก
  • onReceive(Context, Intent): สกัดกั้นการออกอากาศของแพลตฟอร์มทุกรายการก่อนเมธอดเรียกกลับที่เฉพาะเจาะจง คุณต้องตรวจสอบว่าตรรกะตัวรับที่กำหนดเอง ที่คุณเขียนเรียกใช้ super.onReceive(context, intent) และ ต้องไม่เรียกใช้ goAsync ด้วยตนเอง เนื่องจาก Glance จะกำหนดเส้นทางการทำงาน แบบไม่พร้อมกันโดยอัตโนมัติ

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

GlanceAppWidgetReceiver จะกรองและจัดการ Intent การออกอากาศของวิดเจ็ตแพลตฟอร์มพื้นฐานต่อไปนี้เบื้องหลัง

สร้าง UI

ข้อมูลโค้ดต่อไปนี้แสดงวิธีสร้าง UI

/* Import Glance Composables
 In the event there is a name clash with the Compose classes of the same name,
 you may rename the imports per https://kotlinlang.org/docs/packages.html#imports
 using the `as` keyword.

import androidx.glance.Button
import androidx.glance.layout.Column
import androidx.glance.layout.Row
import androidx.glance.text.Text
*/
class MyAppWidget : GlanceAppWidget() {

    override suspend fun provideGlance(context: Context, id: GlanceId) {
        // Load data needed to render the AppWidget.
        // Use `withContext` to switch to another thread for long running
        // operations.

        provideContent {
            // create your AppWidget here
            MyContent()
        }
    }

    @Composable
    private fun MyContent() {
        Column(
            modifier = GlanceModifier.fillMaxSize(),
            verticalAlignment = Alignment.Top,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp))
            Row(horizontalAlignment = Alignment.CenterHorizontally) {
                Button(
                    text = "Home",
                    onClick = actionStartActivity<MyActivity>()
                )
                Button(
                    text = "Work",
                    onClick = actionStartActivity<MyActivity>()
                )
            }
        }
    }
}

ตัวอย่างโค้ดก่อนหน้าจะทำสิ่งต่อไปนี้

  • ใน Column ระดับบนสุด ระบบจะวางรายการในแนวตั้งต่อกัน
  • Column จะขยายขนาดให้ตรงกับพื้นที่ว่าง (ผ่าน GlanceModifier และจัดเนื้อหาให้อยู่ด้านบน (verticalAlignment) และกึ่งกลางในแนวนอน (horizontalAlignment)
  • เนื้อหาของ Column กำหนดโดยใช้แลมบ์ดา ลำดับมีความสำคัญ
    • รายการแรกใน Column คือคอมโพเนนต์ Text ที่มีระยะห่างภายใน 12.dp
    • รายการที่ 2 คือ Row ซึ่งระบบจะวางรายการในแนวนอน ต่อกัน โดยมี Buttons 2 รายการอยู่กึ่งกลางในแนวนอน (horizontalAlignment) การแสดงผลสุดท้ายจะขึ้นอยู่กับพื้นที่ว่าง รูปภาพต่อไปนี้เป็นตัวอย่างลักษณะที่อาจปรากฏ
วิดเจ็ตปลายทาง
รูปที่ 1 ตัวอย่าง UI

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

ใช้มุมแบบมน

Android 12 เปิดตัวพารามิเตอร์ระบบเพื่อปรับแต่งรัศมีมุมของ App Widget แบบไดนามิก

  • system_app_widget_background_radius: ระบุรัศมีมุมของคอนเทนเนอร์พื้นหลังของวิดเจ็ต (ไม่เกิน 28 dp)
  • รัศมีภายใน: หากต้องการป้องกันไม่ให้เนื้อหาถูกตัดออก ให้คำนวณรัศมีตามสัดส่วนสำหรับเนื้อหาภายในโดยอิงตามเส้นขอบพื้นหลังของระบบ ดังนี้ systemRadiusValue - widgetPadding

ใน Glance คุณสามารถใช้พร็อพเพอร์ตี้การปรับขนาดรัศมีมุมแบบไดนามิกในองค์ประกอบได้โดยใช้ GlanceModifier.cornerRadius(android.R.dimen.system_app_widget_background_radius)

หากต้องการความเข้ากันได้แบบย้อนหลังในอุปกรณ์ที่ใช้ Android 11 (ระดับ API 30) หรือต่ำกว่า ให้ใช้แอตทริบิวต์ที่กำหนดเองและการกลับไปใช้ทรัพยากรธีมที่กำหนดเอง

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