สร้าง UI ด้วยข้อมูลโดยย่อ

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

ใช้ Box, Column และ Row

Glance มีเลย์เอาต์ที่ประกอบกันได้ 3 รายการหลักๆ ดังนี้

  • Box: วางองค์ประกอบทับองค์ประกอบอื่น โดยจะเปลี่ยนเป็น RelativeLayout

  • Column: วางองค์ประกอบต่อจากกันในแกนแนวตั้ง ช่วยแปล เป็น LinearLayout ด้วยการวางแนวตั้ง

  • Row: วางองค์ประกอบต่อจากกันในแกนแนวนอน ช่วยแปล เป็นLinearLayoutแบบแนวนอน

Glance รองรับออบเจ็กต์ Scaffold วาง Column, Row และ Composable Box ภายในออบเจ็กต์ Scaffold ที่ระบุ

รูปภาพของเลย์เอาต์คอลัมน์ แถว และกล่อง
รูปที่ 1 ตัวอย่างเลย์เอาต์ที่มีคอลัมน์ แถว และกล่อง

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 ประกอบด้วยการเปิดตัวคอมโพเนนต์เพิ่มเติม ตามที่อธิบายไว้ใน ตารางต่อไปนี้

ชื่อ รูปภาพ ลิงก์อ้างอิง หมายเหตุเพิ่มเติม
ปุ่มแบบเติมพื้น alt_text ส่วนประกอบ
ปุ่มเติมขอบ alt_text ส่วนประกอบ
ปุ่มไอคอน alt_text ส่วนประกอบ หลัก / รอง / ไอคอนเท่านั้น
แถบชื่อ alt_text ส่วนประกอบ
Scaffold นั่งร้านและแถบชื่ออยู่ในการสาธิตเดียวกัน

หากต้องการทราบข้อมูลเฉพาะด้านการออกแบบ โปรดดูการออกแบบคอมโพเนนต์ใน ชุดการออกแบบใน Figma