ส่วนต่อไปนี้จะอธิบายวิธีสร้างวิดเจ็ตแอปพื้นฐานด้วย Glance
ประกาศ AppWidget ในไฟล์ Manifest
หลังจากทำตามขั้นตอนการตั้งค่าเสร็จแล้ว ให้ประกาศ AppWidget และข้อมูลเมตาในแอป
ขยายตัวรับ
AppWidgetจากGlanceAppWidgetReceiver:class MyAppWidgetReceiver : GlanceAppWidgetReceiver() { override val glanceAppWidget: GlanceAppWidget = TODO("Create GlanceAppWidget") }
ลงทะเบียนผู้ให้บริการวิดเจ็ตแอปในไฟล์
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
จากนั้นทำตามคำแนะนำสร้างวิดเจ็ตเพื่อสร้างและกำหนดข้อมูลวิดเจ็ตแอปในไฟล์ @xml/my_app_widget_info
ความแตกต่างเพียงอย่างเดียวสำหรับ Glance คือไม่มี 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 |
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 และต่ำกว่า) |
|
autoAdvanceViewId |
ระบุรหัสการแสดงผลของมุมมองย่อยของวิดเจ็ตที่โฮสต์ของวิดเจ็ตเลื่อนโดยอัตโนมัติ |
widgetCategory |
ประกาศว่าวิดเจ็ตของคุณจะแสดงในหน้าจอหลัก (home_screen) หน้าจอล็อก (keyguard) หรือทั้ง 2 หน้าจอได้หรือไม่ สำหรับ Android 5.0 ขึ้นไป จะใช้ได้เฉพาะ home_screen เท่านั้น |
widgetFeatures |
ประกาศฟีเจอร์ที่วิดเจ็ตรองรับ เช่น หากการกำหนดค่าวิดเจ็ตเป็นแบบไม่บังคับ ให้ระบุทั้ง configuration_optional และ reconfigurable |
จำกัดความ GlanceAppWidget
สร้างคลาสใหม่ที่ขยายจาก
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") } } }
สร้างอินสแตนซ์ใน
glanceAppWidgetบนGlanceAppWidgetReceiverดังนี้class MyAppWidgetReceiver : GlanceAppWidgetReceiver() { // Let MyAppWidgetReceiver know which GlanceAppWidget to use override val glanceAppWidget: GlanceAppWidget = MyAppWidget() }
ตอนนี้คุณได้กำหนดค่า AppWidget โดยใช้ Glance แล้ว
จัดการหน่วยความจำรูปภาพของวิดเจ็ตในที่เก็บข้อมูลขนาดต่างๆ
เมื่อวิดเจ็ตแสดงบิตแมปหรือ URI รูปภาพ content:// ให้รักษาหน่วยความจำรูปภาพ RemoteViews ที่เรียงลำดับทั้งหมดให้อยู่ภายในขีดจำกัดของระบบ (6 * width *
height ไบต์ ซึ่งบังคับใช้ด้วย IllegalArgumentException ใน Android 17 ขึ้นไป) โดยทำดังนี้
- ถอดรหัสบิตแมปที่แชร์ก่อน
provideContent: หากวิดเจ็ตใช้SizeMode.ResponsiveหรือSizeMode.ExactGlance จะเรียกใช้ LambdaprovideContent1 ครั้งสำหรับที่เก็บข้อมูลDpSizeแต่ละรายการ การถอดรหัสBitmapภายในprovideContentจะจัดสรรอินสแตนซ์Bitmapแยกต่างหากสำหรับทุกที่เก็บ ซึ่งจะเพิ่มการใช้งานหน่วยความจำในRemoteViews.mBitmapCacheถอดรหัสบิตแมปที่ลดอัตราสุ่ม (Bitmap.Config.RGB_565สำหรับรูปภาพทึบแสง) 1 ครั้งภายในprovideGlanceก่อนprovideContentเพื่อให้ที่เก็บข้อมูลทั้งหมดใช้การอ้างอิงBitmapเดียวกัน ใช้การนําเข้า
ImageProviderที่ตรงกัน: Jetpack Glance มีโอเวอร์โหลดImageProviderที่แตกต่างกัน 2 รายการใน 2 แพ็กเกจ ดังนี้- ใช้
import androidx.glance.ImageProviderเมื่อส่งผ่านทรัพยากรImageProvider(bitmap: Bitmap)ในหน่วยความจำหรือทรัพยากรที่ถอนออกได้ImageProvider(resId: Int) - ใช้
import androidx.glance.appwidget.ImageProviderเมื่อส่ง URIImageProvider(uri: Uri)เมื่อแสดงไฟล์ที่แคชไว้ผ่าน URIFileProviderให้ให้สิทธิ์Intent.FLAG_GRANT_READ_URI_PERMISSIONแก่แพ็กเกจ LauncherIntent.CATEGORY_HOMEที่ติดตั้งไว้ทุกแพ็กเกจเพื่อให้โฮสต์แสดงผลรูปภาพได้
- ใช้
ใช้คลาส GlanceAppWidgetReceiver เพื่อจัดการการออกอากาศวิดเจ็ต
วิดเจ็ต GlanceAppWidgetReceiver จะประสานงานการออกอากาศและการอัปเดตสถานะแพลตฟอร์มโดยการขยาย AppWidgetProvider ที่สำคัญ โดยจะรับเหตุการณ์ของแพลตฟอร์มเมื่อมีการอัปเดต ลบ เปิดใช้ หรือปิดใช้วิดเจ็ต และแปลเหตุการณ์เหล่านั้นเป็นคำขอวงจรของ Compose
ประกาศวิดเจ็ตในไฟล์ Manifest
ประกาศคลาสย่อยของคลาส GlanceAppWidgetReceiver เป็น Broadcast Receiver ในไฟล์ 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 ซึ่งระบุคลาส Receiver ตัวรับต้องยอมรับการดำเนินการประกาศ ACTION_APPWIDGET_UPDATE ภายใน <intent-filter>
องค์ประกอบ <meta-data> ต้องระบุชื่อเป็น android.appwidget.provider และแอตทริบิวต์ android:resource ต้องชี้ไปยังทรัพยากรข้อมูลเมตา XML ของ AppWidgetProviderInfo (@xml/my_app_widget_info)
ใช้คลาส GlanceAppWidgetReceiver
ใน Glance คุณจะขยาย GlanceAppWidgetReceiver แทน AppWidgetProvider โดยตรง ใช้โดยลิงก์ตัวรับสัญญาณกับอินสแตนซ์ GlanceAppWidget Callback หลักที่ใช้ได้ใน GlanceAppWidgetReceiver จะทำงานดังนี้
onUpdate(): Glance จะลบล้างโดยอัตโนมัติเพื่อดำเนินการอัปเดตองค์ประกอบ หากคุณลบล้างonUpdateด้วยตนเอง คุณต้องเรียกใช้super.onUpdateเพื่ออนุญาตให้ Glance เปิดใช้เทรดการคอมโพสิชันได้สำเร็จonAppWidgetOptionsChanged(): เรียกใช้เมื่อวางหรือปรับขนาดวิดเจ็ตเป็นครั้งแรก Glance จะอ่านรายการแพ็กเกจตัวเลือกเบื้องหลังเพื่อให้เลย์เอาต์ปรับเปลี่ยนได้อย่างราบรื่นตามมิติข้อมูลรันไทม์onDeleted(Context, IntArray): เรียกใช้เมื่อผู้ใช้ลบอินสแตนซ์วิดเจ็ตที่เฉพาะเจาะจงonEnabled(Context): ทริกเกอร์เมื่อสร้างอินสแตนซ์แรกของวิดเจ็ตสำเร็จ เหมาะอย่างยิ่งสำหรับการย้ายข้อมูลทั่วโลกonDisabled(Context): เรียกใช้เมื่ออินสแตนซ์ที่ใช้งานอยู่ล่าสุดของผู้ให้บริการถูกนำออกonReceive(Context, Intent): ดักจับการออกอากาศของแพลตฟอร์มทุกรายการก่อนเมธอด Callback ที่เฉพาะเจาะจง คุณต้องตรวจสอบว่าตรรกะ Receiver ที่กำหนดเองที่คุณเขียนเรียกsuper.onReceive(context, intent)และต้องไม่เรียกgoAsyncด้วยตนเอง เนื่องจาก Glance จะกำหนดเส้นทางงานแบบอะซิงโครนัสโดยอัตโนมัติ
รับ Intent การออกอากาศของวิดเจ็ต
เบื้องหลังการทำงาน GlanceAppWidgetReceiver จะกรองและจัดการ Intent การออกอากาศวิดเจ็ตแพลตฟอร์มพื้นฐานต่อไปนี้
ACTION_APPWIDGET_UPDATEACTION_APPWIDGET_DELETEDACTION_APPWIDGET_ENABLEDACTION_APPWIDGET_DISABLEDACTION_APPWIDGET_OPTIONS_CHANGED
สร้าง 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 ลำดับมีความสำคัญ
คุณสามารถเปลี่ยนค่าการจัดแนวหรือใช้ค่าตัวแก้ไขต่างๆ (เช่น ระยะห่างจากขอบ) เพื่อเปลี่ยนตำแหน่งและขนาดของคอมโพเนนต์ได้ ดูรายการคอมโพเนนต์ พารามิเตอร์ และตัวแก้ไขที่ใช้ได้สำหรับแต่ละคลาสทั้งหมดได้ที่เอกสารอ้างอิง
ใช้มุมโค้งมน
Android 12 เปิดตัวพารามิเตอร์ของระบบเพื่อปรับแต่งรัศมีมุมของวิดเจ็ตแอปแบบไดนามิก ดังนี้
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>