กฎข้อหนึ่งของ Compose คือคุณควรวัดองค์ประกอบย่อยเพียงครั้งเดียว การวัดองค์ประกอบย่อย 2 ครั้งจะทำให้เกิดข้อยกเว้นรันไทม์ อย่างไรก็ตาม บางครั้งคุณอาจต้องการข้อมูลบางอย่างเกี่ยวกับองค์ประกอบย่อยก่อนที่จะวัด
Intrinsics ช่วยให้คุณค้นหาองค์ประกอบย่อยก่อนที่จะวัดจริงได้
คุณสามารถขอ IntrinsicSize.Min หรือ IntrinsicSize.Max ของ Composable ได้ดังนี้
Modifier.width(IntrinsicSize.Min)- ความกว้างขั้นต่ำที่จำเป็นในการแสดงเนื้อหาอย่างเหมาะสมคือเท่าใดModifier.width(IntrinsicSize.Max)- ความกว้างสูงสุดที่คุณต้องการเพื่อแสดงเนื้อหาอย่างเหมาะสมคือเท่าใดModifier.height(IntrinsicSize.Min)- ความสูงขั้นต่ำที่จำเป็นในการแสดงเนื้อหาอย่างเหมาะสมคือเท่าใดModifier.height(IntrinsicSize.Max)- ความสูงสูงสุดที่คุณต้องการเพื่อแสดงเนื้อหาอย่างเหมาะสมคือเท่าใด
ตัวอย่างเช่น หากคุณขอ minIntrinsicHeight ของ Text ที่มีข้อจำกัด width แบบไม่จำกัดในเลย์เอาต์ที่กำหนดเอง ระบบจะแสดงผล height ของ Text โดยมีข้อความที่วาดในบรรทัดเดียว
การทำงานของ Intrinsics
คุณสามารถสร้าง Composable ที่แสดงข้อความ 2 รายการบนหน้าจอโดยคั่นด้วยตัวแบ่งได้ดังนี้
โดยใช้ Row กับ Composable Text 2 รายการที่เติมพื้นที่ว่าง และ Divider ตรงกลาง Divider ควรมีความสูงเท่ากับ Text ที่สูงที่สุด และควรมีความบาง (width = 1.dp)
@Composable fun TwoTexts(modifier: Modifier = Modifier, text1: String, text2: String) { Row(modifier = modifier) { Text( modifier = Modifier .weight(1f) .padding(start = 4.dp) .wrapContentWidth(Alignment.Start), text = text1 ) VerticalDivider( color = Color.Black, modifier = Modifier.fillMaxHeight().width(1.dp) ) Text( modifier = Modifier .weight(1f) .padding(end = 4.dp) .wrapContentWidth(Alignment.End), text = text2 ) } }
Divider ขยายเต็มหน้าจอ ซึ่งไม่ใช่ลักษณะการทำงานที่ต้องการ
ซึ่งเกิดขึ้นเนื่องจาก Row วัดองค์ประกอบย่อยแต่ละรายการแยกกัน และไม่สามารถใช้ความสูงของ Text เพื่อจำกัด Divider ได้
หากต้องการให้ Divider เติมพื้นที่ว่างด้วยความสูงที่กำหนดแทน ให้ใช้ตัวแก้ไข height(IntrinsicSize.Min)
height(IntrinsicSize.Min) ปรับขนาดองค์ประกอบย่อยให้สูงเท่ากับความสูงภายในขั้นต่ำ เนื่องจากตัวแก้ไขนี้เป็นแบบเรียกซ้ำ จึงจะค้นหา minIntrinsicHeight ของ Row และรายการย่อย
การใช้ตัวแก้ไขนี้กับโค้ดจะทำให้โค้ดทำงานได้ตามที่คาดไว้
@Composable fun TwoTexts(modifier: Modifier = Modifier, text1: String, text2: String) { Row(modifier = modifier.height(IntrinsicSize.Min)) { Text( modifier = Modifier .weight(1f) .padding(start = 4.dp) .wrapContentWidth(Alignment.Start), text = text1 ) VerticalDivider( color = Color.Black, modifier = Modifier.fillMaxHeight().width(1.dp) ) Text( modifier = Modifier .weight(1f) .padding(end = 4.dp) .wrapContentWidth(Alignment.End), text = text2 ) } } // @Preview @Composable fun TwoTextsPreview() { MaterialTheme { Surface { TwoTexts(text1 = "Hi", text2 = "there") } } }
เมื่อมีตัวอย่าง
ความสูงของ Row จะกำหนดดังนี้
minIntrinsicHeightของ ComposableRowคือminIntrinsicHeightสูงสุดขององค์ประกอบย่อยminIntrinsicHeightขององค์ประกอบDividerคือ 0 เนื่องจากไม่ใช้พื้นที่หากไม่มีข้อจำกัดTextminIntrinsicHeightคือข้อความสำหรับwidthที่เฉพาะเจาะจง- ดังนั้น ข้อจำกัด
heightขององค์ประกอบRowจึงกลายเป็นค่าสูงสุดminIntrinsicHeightของText - จากนั้น
Dividerจะขยายheightไปยังข้อจำกัดheightที่กำหนดโดยRow
Intrinsics ในเลย์เอาต์ที่กำหนดเอง
เมื่อสร้างตัวแก้ไข Layout หรือ layout ที่กำหนดเอง ระบบจะคำนวณการวัดค่าภายในโดยอัตโนมัติตามค่าประมาณ ดังนั้นการคำนวณจึงอาจไม่ถูกต้องสำหรับเลย์เอาต์บางแบบ API เหล่านี้มีตัวเลือกในการลบล้างค่าเริ่มต้นเหล่านี้
หากต้องการระบุการวัดค่าภายในของ Layout ที่กำหนดเอง ให้ลบล้าง minIntrinsicWidth, minIntrinsicHeight, maxIntrinsicWidth และ maxIntrinsicHeight ของอินเทอร์เฟซ MeasurePolicy เมื่อสร้าง
การลบล้างต้องตรงกับสิ่งที่ measure ทำ ตัวอย่างเช่น ในคอลัมน์ที่กำหนดเองซึ่งวางองค์ประกอบย่อยซ้อนกันในแนวตั้งและกำหนดให้องค์ประกอบย่อยแต่ละรายการมีความกว้างเต็มคอลัมน์ ความสูงที่แท้จริงขั้นต่ำสำหรับ width หนึ่งๆ คือผลรวมของความสูงที่แท้จริงขั้นต่ำขององค์ประกอบย่อยสำหรับ width นั้นๆ
@Composable fun MyCustomComposable( modifier: Modifier = Modifier, content: @Composable () -> Unit ) { Layout( content = content, modifier = modifier, measurePolicy = object : MeasurePolicy { override fun MeasureScope.measure( measurables: List<Measurable>, constraints: Constraints ): MeasureResult { val childConstraints = constraints.copy(minHeight = 0) val placeables = measurables.map { measurable -> measurable.measure(childConstraints) } val width = constraints.constrainWidth( placeables.maxOfOrNull { it.width } ?: 0 ) val height = constraints.constrainHeight( placeables.sumOf { it.height } ) return layout(width, height) { var yPosition = 0 placeables.forEach { placeable -> placeable.placeRelative(x = 0, y = yPosition) yPosition += placeable.height } } } override fun IntrinsicMeasureScope.minIntrinsicHeight( measurables: List<IntrinsicMeasurable>, width: Int ): Int { return measurables.sumOf { measurable -> measurable.minIntrinsicHeight(width) } } // Other intrinsics related methods have a default value, // you can override only the methods that you need. } ) }
เมื่อสร้างตัวแก้ไข layout ที่กำหนดเอง ให้ลบล้างเมธอดที่เกี่ยวข้องในอินเทอร์เฟซ LayoutModifier การคำนวณภายในต้องตรงกับ measure ตัวอย่างเช่น สำหรับ height หนึ่งๆ ตัวแก้ไขที่เพิ่มระยะห่างจากขอบคงที่ในทุกด้านจะลบระยะห่างจากขอบแนวตั้งก่อนที่จะค้นหา minIntrinsicWidth ขององค์ประกอบย่อย และเพิ่มระยะห่างจากขอบแนวนอนลงในผลลัพธ์
fun Modifier.myCustomModifier(padding: Dp = 8.dp) = this then object : LayoutModifier { override fun MeasureScope.measure( measurable: Measurable, constraints: Constraints ): MeasureResult { val paddingPx = padding.roundToPx() val horizontal = paddingPx * 2 val vertical = paddingPx * 2 val placeable = measurable.measure( constraints.offset(-horizontal, -vertical) ) val width = constraints.constrainWidth(placeable.width + horizontal) val height = constraints.constrainHeight(placeable.height + vertical) return layout(width, height) { placeable.place(paddingPx, paddingPx) } } override fun IntrinsicMeasureScope.minIntrinsicWidth( measurable: IntrinsicMeasurable, height: Int ): Int { val paddingPx = padding.roundToPx() val horizontal = paddingPx * 2 val vertical = paddingPx * 2 val childHeight = if (height == Constraints.Infinity) { Constraints.Infinity } else { (height - vertical).coerceAtLeast(0) } return measurable.minIntrinsicWidth(childHeight) + horizontal } // Other intrinsics related methods have a default value, // you can override only the methods that you need. }
แนะนำสำหรับคุณ
- หมายเหตุ: ข้อความลิงก์จะแสดงเมื่อ JavaScript ปิดอยู่
- เลย์เอาต์ที่กำหนดเอง {:#custom-layouts}
- เส้นการจัดแนวใน Jetpack Compose
- ระยะต่างๆ ของ Jetpack Compose