เลย์เอาต์ที่กำหนดเอง

ใน Compose องค์ประกอบ UI จะแสดงด้วยฟังก์ชันคอมโพสิเบิลที่แสดง UI บางส่วนเมื่อเรียกใช้ จากนั้นระบบจะเพิ่มลงในต้นไม้ UI ที่แสดงผลบนหน้าจอ องค์ประกอบ UI แต่ละรายการมีองค์ประกอบหลัก 1 รายการและอาจมีองค์ประกอบย่อยได้หลายรายการ องค์ประกอบแต่ละรายการยังอยู่ในองค์ประกอบหลักด้วย ซึ่งระบุเป็นตำแหน่ง (x, y) และขนาดที่ระบุเป็น width และ height

องค์ประกอบหลักจะกำหนดข้อจำกัดสำหรับองค์ประกอบย่อย ระบบจะขอให้องค์ประกอบกำหนดขนาดภายในข้อจำกัดเหล่านั้น ข้อจำกัดจะจำกัด width และ height ขั้นต่ำและสูงสุดขององค์ประกอบ หากองค์ประกอบมีองค์ประกอบย่อย ระบบอาจวัดองค์ประกอบย่อยแต่ละรายการเพื่อช่วยระบุขนาดขององค์ประกอบนั้น เมื่อองค์ประกอบหนึ่งๆ กำหนดและรายงานขนาดของตัวเองแล้ว องค์ประกอบนั้นจะมีสิทธิ์กำหนดวิธีวางองค์ประกอบย่อยของตนโดยสัมพันธ์กับตัวเอง ตามที่อธิบายไว้อย่างละเอียดในการสร้างเลย์เอาต์ที่กำหนดเอง

การจัดวางโหนดแต่ละโหนดในต้นไม้ UI เป็นกระบวนการ 3 ขั้นตอน โหนดแต่ละโหนดต้องมีลักษณะดังนี้

  1. วัดรายการย่อย
  2. กำหนดขนาดของตัวเอง
  3. วางรายการย่อย

เลย์เอาต์โหนด 3 ขั้นตอน ได้แก่ วัดขนาดของโหนดย่อย เลือกขนาด และวางโหนดย่อย

การใช้ขอบเขตจะกำหนดเวลาที่คุณวัดและวางตำแหน่งบุตรหลานได้ การวัดเลย์เอาต์ทำได้ในระหว่างการวัดและการวางเลย์เอาต์เท่านั้น และสามารถวางรายการย่อยได้ในระหว่างการวางเลย์เอาต์เท่านั้น (และหลังจากวัดแล้วเท่านั้น) เนื่องจากขอบเขตการคอมไพล์ เช่น MeasureScope และ PlacementScope จะมีการบังคับใช้ข้อจำกัดนี้เมื่อคอมไพล์

ใช้ตัวแก้ไขเลย์เอาต์

คุณสามารถใช้ตัวแก้ไข layout เพื่อแก้ไขวิธีวัดและวางองค์ประกอบ Layout คือ Lambda ซึ่งพารามิเตอร์ประกอบด้วยองค์ประกอบที่วัดได้ซึ่งส่งผ่านเป็น measurable และข้อจำกัดขาเข้าของคอมโพสิเบิลนั้นซึ่งส่งผ่านเป็น constraints ตัวปรับแต่งเลย์เอาต์ที่กำหนดเองอาจมีลักษณะดังนี้

fun Modifier.customLayoutModifier() =
    layout { measurable, constraints ->
        // ...
    }

มาแสดง Text บนหน้าจอและควบคุมระยะทางจากด้านบนถึงบรรทัดฐานของบรรทัดแรกกัน การดำเนินการนี้ตรงกับสิ่งที่ตัวแก้ไข paddingFromBaseline ทำได้ เรากำลังนำตัวอย่างนี้ไปใช้ ซึ่งทำได้โดยใช้ตัวแก้ไข layout เพื่อวางคอมโพสิเบิลบนหน้าจอด้วยตนเอง ลักษณะการทำงานที่ต้องการเมื่อTextตั้งค่าระยะห่างจากขอบด้านบน24.dpมีดังนี้

แสดงความแตกต่างระหว่างระยะขอบ UI ปกติซึ่งกำหนดระยะห่างระหว่างองค์ประกอบกับระยะขอบข้อความที่กำหนดระยะห่างจากบรรทัดฐานหนึ่งไปยังอีกบรรทัดหนึ่ง

โค้ดในการสร้างการเว้นวรรคมีดังนี้

fun Modifier.firstBaselineToTop(
    firstBaselineToTop: Dp
) = layout { measurable, constraints ->
    // Measure the composable
    val placeable = measurable.measure(constraints)

    // Check the composable has a first baseline
    check(placeable[FirstBaseline] != AlignmentLine.Unspecified)
    val firstBaseline = placeable[FirstBaseline]

    // Height of the composable with padding - first baseline
    val placeableY = firstBaselineToTop.roundToPx() - firstBaseline
    val height = placeable.height + placeableY
    layout(placeable.width, height) {
        // Where the composable gets placed
        placeable.placeRelative(0, placeableY)
    }
}

สิ่งที่เกิดขึ้นในโค้ดดังกล่าวมีดังนี้

  1. ในพารามิเตอร์ measurable lambda คุณจะวัด Text ที่แสดงโดยพารามิเตอร์ที่วัดได้โดยการเรียกใช้ measurable.measure(constraints)
  2. คุณระบุขนาดของ Composable ได้โดยเรียกใช้layout(width, height) เมธอด ซึ่งจะให้ Lambda ที่ใช้วางองค์ประกอบที่รวมไว้ด้วย ในกรณีนี้ ระยะห่างคือความสูงระหว่างบรรทัดฐานสุดท้ายกับระยะห่างจากขอบด้านบนที่เพิ่ม
  3. คุณสามารถจัดตําแหน่งองค์ประกอบที่ตัดขึ้นบรรทัดใหม่บนหน้าจอได้โดยเรียกใช้ placeable.place(x, y) หากไม่ได้วางองค์ประกอบที่ตัดขึ้นบรรทัดใหม่ องค์ประกอบเหล่านั้นจะมองไม่เห็น yตำแหน่งสอดคล้องกับระยะห่างจากขอบด้านบน ซึ่งเป็นตำแหน่งของบรรทัดฐานแรกในข้อความ

หากต้องการยืนยันว่าการดัดแปลงนี้ทํางานตามที่คาดไว้ ให้ใช้ตัวแก้ไขนี้ใน Text

@Preview
@Composable
fun TextWithPaddingToBaselinePreview() {
    MyApplicationTheme {
        Text("Hi there!", Modifier.firstBaselineToTop(32.dp))
    }
}

@Preview
@Composable
fun TextWithNormalPaddingPreview() {
    MyApplicationTheme {
        Text("Hi there!", Modifier.padding(top = 32.dp))
    }
}

ตัวอย่างองค์ประกอบข้อความหลายรายการ โดยตัวอย่างหนึ่งแสดงระยะห่างจากขอบปกติระหว่างองค์ประกอบ ส่วนอีกตัวอย่างหนึ่งแสดงระยะห่างจากขอบบรรทัดแรกถึงบรรทัดถัดไป

สร้างเลย์เอาต์ที่กำหนดเอง

ตัวแก้ไข layout จะเปลี่ยนเฉพาะคอมโพสิชันการโทรเท่านั้น หากต้องการวัดและจัดเลย์เอาต์คอมโพสิเบิลหลายรายการ ให้ใช้คอมโพสิเบิล Layout แทน คอมโพสิเบิลนี้ช่วยให้คุณวัดและวางองค์ประกอบย่อยด้วยตนเองได้ เลย์เอาต์ระดับที่สูงขึ้นทั้งหมด เช่น Column และ Row สร้างขึ้นด้วยคอมโพสิชัน Layout

มาสร้าง Column เวอร์ชันพื้นฐานกัน เลย์เอาต์ที่กำหนดเองส่วนใหญ่จะเป็นไปตามรูปแบบนี้

@Composable
fun MyBasicColumn(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit
) {
    Layout(
        modifier = modifier,
        content = content
    ) { measurables, constraints ->
        // measure and position children given constraints logic here
        // ...
    }
}

เช่นเดียวกับตัวแก้ไข layout measurables คือรายการของรายการย่อยที่ต้องวัด และ constraints คือข้อจำกัดจากรายการหลัก MyBasicColumn สามารถใช้ตามตรรกะเดียวกันกับก่อนหน้านี้ได้ดังนี้

@Composable
fun MyBasicColumn(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit
) {
    Layout(
        modifier = modifier,
        content = content
    ) { measurables, constraints ->
        // Don't constrain child views further, measure them with given constraints
        // List of measured children
        val placeables = measurables.map { measurable ->
            // Measure each children
            measurable.measure(constraints)
        }

        // Set the size of the layout as big as it can
        layout(constraints.maxWidth, constraints.maxHeight) {
            // Track the y co-ord we have placed children up to
            var yPosition = 0

            // Place children in the parent layout
            placeables.forEach { placeable ->
                // Position item on the screen
                placeable.placeRelative(x = 0, y = yPosition)

                // Record the y co-ord placed up to
                yPosition += placeable.height
            }
        }
    }
}

คอมโพสิชันย่อยถูกจํากัดด้วยข้อจํากัด Layout (ไม่มีข้อจํากัด minHeight) และวางตาม yPosition ของคอมโพสิชันก่อนหน้า

วิธีใช้คอมโพสิชันที่กําหนดเองมีดังนี้

@Composable
fun CallingComposable(modifier: Modifier = Modifier) {
    MyBasicColumn(modifier.padding(8.dp)) {
        Text("MyBasicColumn")
        Text("places items")
        Text("vertically.")
        Text("We've done it by hand!")
    }
}

เอลิเมนต์ข้อความหลายรายการซ้อนกันไว้ในคอลัมน์

ทิศทางของเลย์เอาต์

เปลี่ยนการวางแนวเลย์เอาต์ของคอมโพสิเบิลโดยเปลี่ยนค่าLocalLayoutDirection ขององค์ประกอบในท้องถิ่น

หากคุณวาง Composable บนหน้าจอด้วยตนเอง LayoutDirection จะเป็นส่วนหนึ่งของ LayoutScope ของ Modifier layout หรือ Composable Layout

เมื่อใช้ layoutDirection ให้วางคอมโพสิเบิลโดยใช้ place place จะไม่เปลี่ยนแปลงตามทิศทางของเลย์เอาต์ (จากซ้ายไปขวาหรือจากขวาไปซ้าย) ต่างจากวิธี placeRelative

การใช้เลย์เอาต์ที่กำหนดเอง

ดูข้อมูลเพิ่มเติมเกี่ยวกับเลย์เอาต์และตัวแก้ไขได้ในหัวข้อเลย์เอาต์พื้นฐานในเครื่องมือเขียน และดูตัวอย่างการใช้งานเลย์เอาต์ที่กำหนดเองได้ในหัวข้อตัวอย่างการเขียนที่สร้างเลย์เอาต์ที่กำหนดเอง

ดูข้อมูลเพิ่มเติม

ดูข้อมูลเพิ่มเติมเกี่ยวกับเลย์เอาต์ที่กำหนดเองในเครื่องมือเขียนได้ที่แหล่งข้อมูลเพิ่มเติมต่อไปนี้

วิดีโอ