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

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

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

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

  1. วัดรายการย่อยทั้งหมด
  2. กำหนดขนาดเอง
  3. วางรายการย่อย
เลย์เอาต์โหนด 3 ขั้นตอน ได้แก่ วัดขนาดองค์ประกอบย่อย กำหนดขนาด และวางองค์ประกอบย่อย
รูปที่ 1 เลย์เอาต์โหนดมี 3 ขั้นตอน ได้แก่ วัดขนาดองค์ประกอบย่อย กำหนดขนาด และวางองค์ประกอบย่อย

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

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

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

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

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

แสดงความแตกต่างระหว่างระยะห่างจากขอบ UI ปกติ ซึ่งกำหนดระยะห่างระหว่างองค์ประกอบต่างๆ กับระยะห่างจากขอบข้อความที่กำหนดระยะห่างจากเส้นฐานหนึ่งไปยังเส้นฐานถัดไป
รูปที่ 2 ใช้ข้อความที่มี paddingFromBaseline แล้ว

โค้ดสำหรับสร้างระยะห่างดังกล่าวมีดังนี้

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. ในพารามิเตอร์ lambda measurable คุณจะวัด Text ที่แสดงโดยพารามิเตอร์ที่วัดได้โดยการเรียก measurable.measure(constraints)
  2. คุณระบุขนาดของ Composable โดยเรียกใช้เมธอด layout(width, height) ซึ่งจะให้ Lambda ที่ใช้สำหรับวางองค์ประกอบที่รวมไว้ด้วย ในกรณีนี้ ความสูงคือความสูงของ Text บวก placeableY ซึ่งเท่ากับ firstBaselineToTop ลบด้วยเส้นฐานแรกของ Text
  3. คุณวางตำแหน่งองค์ประกอบที่รวมไว้บนหน้าจอโดยเรียกใช้ placeable.placeRelative(x, y) หากไม่ได้วางองค์ประกอบที่รวมไว้ องค์ประกอบเหล่านั้นจะไม่ปรากฏ ในเลย์เอาต์จากขวาไปซ้าย placeRelative จะสลับตำแหน่งกับ x โปรดดูทิศทางเลย์เอาต์ ตำแหน่ง y คือ firstBaselineToTop ลบด้วยเส้นฐานแรกของข้อความ เพื่อให้เส้นฐานแรกอยู่ห่างจากด้านบน firstBaselineToTop

หากต้องการยืนยันว่าฟีเจอร์นี้ทำงานตามที่คาดไว้ ให้ใช้ตัวแก้ไขนี้ใน 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))
    }
}

ตัวอย่างองค์ประกอบของข้อความหลายรายการ โดยรายการหนึ่งแสดงระยะห่างจากขอบตามปกติระหว่างองค์ประกอบต่างๆ ส่วนอีกรายการแสดงระยะห่างจากขอบจากเส้นฐานหนึ่งไปยังเส้นฐานถัดไป
รูปที่ 3 ตัวปรับแต่งที่ใช้กับ Composable Text และแสดงตัวอย่าง

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

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

ตัวอย่างนี้จะสร้าง 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 ที่เข้ามา และจะถูกจัดวางตาม yPosition ของคอมโพสิเบิลก่อนหน้า

วิธีใช้ Composable ที่กำหนดเองดังกล่าวมีดังนี้

@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!")
    }
}

องค์ประกอบของข้อความหลายรายการซ้อนกันในคอลัมน์
รูปที่ 4 การใช้งาน Column ที่กำหนดเอง

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

เปลี่ยนทิศทางเลย์เอาต์ของ Composable โดยเปลี่ยน LocalLayoutDirection Composition Local

หากคุณวาง Composable บนหน้าจอด้วยตนเอง LayoutDirection จะพร้อมใช้งานจากพร็อพเพอร์ตี้ layoutDirection ของ MeasureScope ในตัวแก้ไข layout หรือ Composable Layout

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

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

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

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

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

วิดีโอ