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

ส่วนต่อไปนี้จะอธิบายวิธีสร้าง 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 กำหนดกิจกรรมการกำหนดค่าที่จะเปิดขึ้นเมื่อผู้ใช้เพิ่มวิดเจ็ต ดูคู่มือเปิดโอกาสให้ผู้ใช้กำหนดค่า App Widget
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 ซึ่งเป็นเมธอดที่คุณสามารถโหลดข้อมูลที่จำเป็นต่อการแสดงผลวิดเจ็ตได้

    ได้ที่ใช้ Coroutines เพื่อความปลอดภัยในเทรดหลัก

    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 กำหนดโดยใช้ Lambda ลำดับมีความสำคัญ
    • รายการแรกใน 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>