หน้านี้จะอธิบายวิธีจัดการขนาดและปรับขนาดให้ยืดหยุ่นและปรับเปลี่ยนตามอุปกรณ์ ด้วยข้อมูลโดยย่อ โดยใช้คอมโพเนนต์ข้อมูลโดยย่อที่มีอยู่
ใช้ Box
, Column
และ Row
Glance มีเลย์เอาต์ที่ประกอบกันได้ 3 รายการหลักๆ ดังนี้
Box
: วางองค์ประกอบทับองค์ประกอบอื่น โดยจะเปลี่ยนเป็นRelativeLayout
Column
: วางองค์ประกอบต่อจากกันในแกนแนวตั้ง ช่วยแปล เป็นLinearLayout
ด้วยการวางแนวตั้งRow
: วางองค์ประกอบต่อจากกันในแกนแนวนอน ช่วยแปล เป็นLinearLayout
แบบแนวนอน
Glance รองรับออบเจ็กต์ Scaffold
วาง Column
, Row
และ
Composable Box
ภายในออบเจ็กต์ Scaffold
ที่ระบุ
Composable แต่ละรายการเหล่านี้จะช่วยให้คุณกำหนดการจัดข้อความแนวตั้งและแนวนอนได้ ของเนื้อหาและขีดจำกัดความกว้าง ความสูง น้ำหนัก หรือระยะห่างจากขอบโดยใช้ แป้นกดร่วม นอกจากนี้ เด็กแต่ละคนจะกำหนดแป้นกดร่วมเพื่อเปลี่ยนการเว้นวรรคได้ และตำแหน่งภายในระดับบนสุด
ตัวอย่างต่อไปนี้แสดงวิธีสร้าง Row
ที่กระจายอย่างเท่าๆ กัน
ของบุตรหลานในแนวนอน ดังที่แสดงในรูปที่ 1:
Row(modifier = GlanceModifier.fillMaxWidth().padding(16.dp)) { val modifier = GlanceModifier.defaultWeight() Text("first", modifier) Text("second", modifier) Text("third", modifier) }
Row
แสดงเต็มความกว้างสูงสุดที่ใช้ได้ และเนื่องจากรายการย่อยแต่ละรายการมีความกว้างเท่ากัน
แต่ก็จะแบ่งพื้นที่ที่มีอยู่เท่าๆ กัน คุณสามารถกำหนดน้ำหนักที่แตกต่างกัน
ขนาด ระยะห่างจากขอบ หรือการจัดข้อความเพื่อปรับเลย์เอาต์ให้ตรงกับความต้องการ
ใช้เลย์เอาต์ที่เลื่อนได้
อีกวิธีหนึ่งในการมอบเนื้อหาที่ปรับเปลี่ยนตามอุปกรณ์คือการทำให้เนื้อหาเลื่อนได้ นี่คือ
ที่เป็นไปได้ด้วย LazyColumn
Composable Composable นี้ให้คุณกำหนดชุด
ที่จะแสดงภายในคอนเทนเนอร์แบบเลื่อนได้ในวิดเจ็ตแอป
ข้อมูลโค้ดต่อไปนี้แสดงวิธีต่างๆ ในการกำหนดรายการภายในแท็ก
LazyColumn
คุณระบุจำนวนรายการได้โดยทำดังนี้
// Remember to import Glance Composables // import androidx.glance.appwidget.layout.LazyColumn LazyColumn { items(10) { index: Int -> Text( text = "Item $index", modifier = GlanceModifier.fillMaxWidth() ) } }
ระบุแต่ละรายการดังนี้
LazyColumn { item { Text("First Item") } item { Text("Second Item") } }
ระบุรายการหรืออาร์เรย์รายการ ดังนี้
LazyColumn { items(peopleNameList) { name -> Text(name) } }
นอกจากนี้ คุณยังสามารถใช้ทั้งตัวอย่างก่อนหน้านี้ร่วมกันได้
LazyColumn { item { Text("Names:") } items(peopleNameList) { name -> Text(name) } // or in case you need the index: itemsIndexed(peopleNameList) { index, person -> Text("$person at index $index") } }
โปรดทราบว่าข้อมูลโค้ดก่อนหน้าไม่ได้ระบุ itemId
การระบุฟิลด์
itemId
ช่วยปรับปรุงประสิทธิภาพและคงการเลื่อนการเลื่อน
ตำแหน่งผ่านรายการและอัปเดต appWidget
รายการตั้งแต่ Android 12 เป็นต้นไป (สำหรับ
เช่น เมื่อเพิ่มหรือนำรายการออกจากรายการ) ตัวอย่างต่อไปนี้
แสดงวิธีระบุ itemId
items(items = peopleList, key = { person -> person.id }) { person -> Text(person.name) }
นิยาม SizeMode
AppWidget
ขนาดอาจแตกต่างกันไปตามอุปกรณ์ ตัวเลือกของผู้ใช้ หรือ Launcher
คุณจึงต้องมีเค้าโครงที่ยืดหยุ่นดังที่อธิบายไว้ใน ให้
และเลย์เอาต์วิดเจ็ตที่ยืดหยุ่นได้ Glance ทำให้การดำเนินการนี้ง่ายขึ้นด้วย SizeMode
และค่า LocalSize
ส่วนต่อไปนี้จะอธิบายเกี่ยวกับ
โหมดต่างๆ
SizeMode.Single
SizeMode.Single
คือโหมดเริ่มต้น โดยระบุว่ามีเพียงประเภทเดียว
ที่จัดเตรียมไว้ให้ นั่นคือแม้ว่า AppWidget
จะมีการเปลี่ยนแปลงขนาด
ขนาดของเนื้อหาจะไม่มีการเปลี่ยนแปลง
class MyAppWidget : GlanceAppWidget() { override val sizeMode = SizeMode.Single override suspend fun provideGlance(context: Context, id: GlanceId) { // ... provideContent { MyContent() } } @Composable private fun MyContent() { // Size will be the minimum size or resizable // size defined in the App Widget metadata val size = LocalSize.current // ... } }
เมื่อใช้โหมดนี้ โปรดตรวจสอบสิ่งต่อไปนี้
- ค่าข้อมูลเมตาต่ำสุดและสูงสุดจะกำหนดอย่างถูกต้องตาม เกี่ยวกับขนาดของเนื้อหา
- เนื้อหามีความยืดหยุ่นพอในช่วงขนาดที่คาดไว้
โดยทั่วไป คุณควรใช้โหมดนี้ในกรณีต่อไปนี้
ก) AppWidget
มีขนาดคงที่ หรือ
ข) จะไม่เปลี่ยนแปลงเนื้อหาเมื่อปรับขนาด
SizeMode.Responsive
โหมดนี้เทียบเท่ากับการให้เลย์เอาต์ที่ปรับเปลี่ยนตามอุปกรณ์ ซึ่งช่วยให้
GlanceAppWidget
เพื่อกําหนดชุดเลย์เอาต์ที่ปรับเปลี่ยนตามอุปกรณ์ซึ่งมีขอบเขตโดย
ขนาดต่างๆ สำหรับแต่ละขนาดที่กำหนดไว้ จะมีการสร้างเนื้อหาและจับคู่กับ
ขนาดเมื่อสร้างหรืออัปเดต AppWidget
จากนั้นเลือก
เหมาะสมที่สุดตามขนาดที่มี
ตัวอย่างเช่น ในปลายทาง AppWidget
คุณสามารถกำหนดขนาดได้ 3 ขนาด
เนื้อหา:
class MyAppWidget : GlanceAppWidget() { companion object { private val SMALL_SQUARE = DpSize(100.dp, 100.dp) private val HORIZONTAL_RECTANGLE = DpSize(250.dp, 100.dp) private val BIG_SQUARE = DpSize(250.dp, 250.dp) } override val sizeMode = SizeMode.Responsive( setOf( SMALL_SQUARE, HORIZONTAL_RECTANGLE, BIG_SQUARE ) ) override suspend fun provideGlance(context: Context, id: GlanceId) { // ... provideContent { MyContent() } } @Composable private fun MyContent() { // Size will be one of the sizes defined above. val size = LocalSize.current Column { if (size.height >= BIG_SQUARE.height) { Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp)) } Row(horizontalAlignment = Alignment.CenterHorizontally) { Button() Button() if (size.width >= HORIZONTAL_RECTANGLE.width) { Button("School") } } if (size.height >= BIG_SQUARE.height) { Text(text = "provided by X") } } } }
ในตัวอย่างก่อนหน้านี้ มีการเรียกเมธอด provideContent
3 ครั้งและ
จับคู่กับขนาดที่กำหนดแล้ว
- ในการเรียกใช้ครั้งแรก ขนาดจะประเมินเป็น
100x100
เนื้อหาไม่ได้ ใส่ปุ่มเพิ่มเติม หรือข้อความด้านบนและด้านล่างก็ได้ - ในการเรียกใช้ครั้งที่ 2 ขนาดจะประเมินเป็น
250x100
เนื้อหาประกอบด้วย ปุ่มพิเศษ แต่ไม่รวมถึงข้อความด้านบนและด้านล่าง - ในการเรียกครั้งที่ 3 ขนาดจะประเมินเป็น
250x250
เนื้อหาประกอบด้วย ปุ่มพิเศษและข้อความทั้งสอง
SizeMode.Responsive
เป็นการผสมผสานระหว่างอีก 2 โหมดเข้าด้วยกัน และ
กำหนดเนื้อหาที่ปรับเปลี่ยนตามอุปกรณ์ภายในขอบเขตที่กำหนดไว้ล่วงหน้า โดยทั่วไปโหมดนี้
มีประสิทธิภาพดีขึ้นและช่วยให้การเปลี่ยนภาพเป็นไปอย่างราบรื่นมากขึ้นเมื่อปรับขนาด AppWidget
ตารางต่อไปนี้แสดงค่าของขนาดโดยขึ้นอยู่กับ SizeMode
และ
ขนาดที่มี AppWidget
พร้อมใช้งาน:
ขนาดที่มีจำหน่าย | ขนาด 105 X 110 | 203 X 112 | 72 x 72 | 203 X 150 |
---|---|---|---|---|
SizeMode.Single |
ขนาด 110 X 110 | ขนาด 110 X 110 | ขนาด 110 X 110 | ขนาด 110 X 110 |
SizeMode.Exact |
ขนาด 105 X 110 | 203 X 112 | 72 x 72 | 203 X 150 |
SizeMode.Responsive |
80 X 100 | 80 X 100 | 80 X 100 | 150 X 120 |
* ค่าที่แน่นอนมีไว้สำหรับการสาธิตเท่านั้น |
SizeMode.Exact
SizeMode.Exact
เทียบเท่ากับการระบุเลย์เอาต์ที่แน่นอน ซึ่ง
ขอเนื้อหา GlanceAppWidget
ทุกครั้งที่มีขนาด AppWidget
ที่มีอยู่
(เช่น เมื่อผู้ใช้ปรับขนาด AppWidget
ในหน้าจอหลัก)
ตัวอย่างเช่น ในวิดเจ็ตปลายทาง คุณสามารถเพิ่มปุ่มพิเศษได้หาก ความกว้างที่ใช้ได้มากกว่าค่าที่กำหนด
class MyAppWidget : GlanceAppWidget() { override val sizeMode = SizeMode.Exact override suspend fun provideGlance(context: Context, id: GlanceId) { // ... provideContent { MyContent() } } @Composable private fun MyContent() { // Size will be the size of the AppWidget val size = LocalSize.current Column { Text(text = "Where to?", modifier = GlanceModifier.padding(12.dp)) Row(horizontalAlignment = Alignment.CenterHorizontally) { Button() Button() if (size.width > 250.dp) { Button("School") } } } } }
โหมดนี้ให้ความยืดหยุ่นมากกว่าโหมดอื่นๆ ข้อควรระวัง:
- คุณต้องสร้าง
AppWidget
ขึ้นใหม่ทั้งหมดทุกครั้งที่มีการเปลี่ยนแปลงขนาด ช่วงเวลานี้ อาจทำให้เกิดปัญหาด้านประสิทธิภาพและ UI ที่เพิ่มขึ้นเมื่อเนื้อหาซับซ้อน - ขนาดที่ใช้ได้อาจแตกต่างกันไปตามการใช้งาน Launcher ตัวอย่างเช่น หากตัวเรียกใช้งานไม่มีรายการขนาด ขนาดขั้นต่ำ ขนาดที่ใช้ได้
- ในอุปกรณ์ Android 12 ล่วงหน้า ตรรกะการคำนวณขนาดอาจใช้งานไม่ได้ในบางอุปกรณ์ เท่านั้น
โดยทั่วไปแล้ว คุณควรใช้โหมดนี้หากไม่สามารถใช้ SizeMode.Responsive
(กล่าวคือ จะใช้เลย์เอาต์ที่ปรับเปลี่ยนตามอุปกรณ์ชุดเล็กๆ ไม่ได้)
เข้าถึงทรัพยากร
ใช้ LocalContext.current
เพื่อเข้าถึงทรัพยากร Android ดังที่แสดงใน
ตัวอย่างต่อไปนี้
LocalContext.current.getString(R.string.glance_title)
เราขอแนะนำให้ระบุรหัสทรัพยากรโดยตรงเพื่อลดขนาดของข้อมูลสุดท้าย
RemoteViews
และเพื่อเปิดใช้ทรัพยากรแบบไดนามิก เช่น ไดนามิก
สี [color]
Composable และเมธอดจะยอมรับทรัพยากรโดยใช้ "provider" เช่น
ImageProvider
หรือใช้วิธีการโอเวอร์โหลด เช่น
GlanceModifier.background(R.color.blue)
เช่น
Column( modifier = GlanceModifier.background(R.color.default_widget_background) ) { /**...*/ } Image( provider = ImageProvider(R.drawable.ic_logo), contentDescription = "My image", )
ข้อความของแฮนเดิล
ข้อมูลโดยย่อ 1.1.0 มี API สำหรับตั้งค่ารูปแบบข้อความ ตั้งค่ารูปแบบข้อความโดยใช้
แอตทริบิวต์ fontSize
, fontWeight
หรือ fontFamily
ของคลาส TextStyle
fontFamily
รองรับแบบอักษรของระบบทั้งหมด ดังที่แสดงในตัวอย่างต่อไปนี้ แต่
แบบอักษรที่กำหนดเองในแอปจะใช้ไม่ได้
Text(
style = TextStyle(
fontWeight = FontWeight.Bold,
fontSize = 18.sp,
fontFamily = FontFamily.Monospace
),
text = "Example Text"
)
เพิ่มปุ่มผสม
เราเปิดตัวปุ่มแบบรวมใน Android 12 ข้อมูลโดยย่อรองรับการทำงานย้อนหลัง ความเข้ากันได้ของปุ่มผสมประเภทต่อไปนี้
ปุ่มผสมเหล่านี้แต่ละปุ่มจะแสดงมุมมองที่คลิกได้ซึ่งแสดงถึง "เลือกแล้ว"
var isApplesChecked by remember { mutableStateOf(false) } var isEnabledSwitched by remember { mutableStateOf(false) } var isRadioChecked by remember { mutableStateOf(0) } CheckBox( checked = isApplesChecked, onCheckedChange = { isApplesChecked = !isApplesChecked }, text = "Apples" ) Switch( checked = isEnabledSwitched, onCheckedChange = { isEnabledSwitched = !isEnabledSwitched }, text = "Enabled" ) RadioButton( checked = isRadioChecked == 1, onClick = { isRadioChecked = 1 }, text = "Checked" )
เมื่อสถานะเปลี่ยนแปลง ระบบจะทริกเกอร์ lambda ที่ระบุ คุณสามารถจัดเก็บ สถานะการตรวจสอบ ดังที่ปรากฏในตัวอย่างต่อไปนี้
class MyAppWidget : GlanceAppWidget() { override suspend fun provideGlance(context: Context, id: GlanceId) { val myRepository = MyRepository.getInstance() provideContent { val scope = rememberCoroutineScope() val saveApple: (Boolean) -> Unit = { scope.launch { myRepository.saveApple(it) } } MyContent(saveApple) } } @Composable private fun MyContent(saveApple: (Boolean) -> Unit) { var isAppleChecked by remember { mutableStateOf(false) } Button( text = "Save", onClick = { saveApple(isAppleChecked) } ) } }
คุณยังระบุแอตทริบิวต์ colors
ให้กับ CheckBox
, Switch
และ
RadioButton
เพื่อปรับแต่งสี:
CheckBox( // ... colors = CheckboxDefaults.colors( checkedColor = ColorProvider(day = colorAccentDay, night = colorAccentNight), uncheckedColor = ColorProvider(day = Color.DarkGray, night = Color.LightGray) ), checked = isChecked, onCheckedChange = { isChecked = !isChecked } ) Switch( // ... colors = SwitchDefaults.colors( checkedThumbColor = ColorProvider(day = Color.Red, night = Color.Cyan), uncheckedThumbColor = ColorProvider(day = Color.Green, night = Color.Magenta), checkedTrackColor = ColorProvider(day = Color.Blue, night = Color.Yellow), uncheckedTrackColor = ColorProvider(day = Color.Magenta, night = Color.Green) ), checked = isChecked, onCheckedChange = { isChecked = !isChecked }, text = "Enabled" ) RadioButton( // ... colors = RadioButtonDefaults.colors( checkedColor = ColorProvider(day = Color.Cyan, night = Color.Yellow), uncheckedColor = ColorProvider(day = Color.Red, night = Color.Blue) ), )
คอมโพเนนต์เพิ่มเติม
ข้อมูลโดยย่อ 1.1.0 ประกอบด้วยการเปิดตัวคอมโพเนนต์เพิ่มเติม ตามที่อธิบายไว้ใน ตารางต่อไปนี้
ชื่อ | รูปภาพ | ลิงก์อ้างอิง | หมายเหตุเพิ่มเติม |
---|---|---|---|
ปุ่มแบบเติมพื้น | ส่วนประกอบ | ||
ปุ่มเติมขอบ | ส่วนประกอบ | ||
ปุ่มไอคอน | ส่วนประกอบ | หลัก / รอง / ไอคอนเท่านั้น | |
แถบชื่อ | ส่วนประกอบ | ||
Scaffold | นั่งร้านและแถบชื่ออยู่ในการสาธิตเดียวกัน |
หากต้องการทราบข้อมูลเฉพาะด้านการออกแบบ โปรดดูการออกแบบคอมโพเนนต์ใน ชุดการออกแบบใน Figma