เลย์เอาต์โฟลว์ใน Compose

FlowRow และ FlowColumn เป็น Composable ที่คล้ายกับ Row และ Column แต่แตกต่างกันตรงที่รายการจะไหลไปยังบรรทัดถัดไปเมื่อคอนเทนเนอร์ไม่มีพื้นที่เหลือ ซึ่งจะสร้างแถวหรือคอลัมน์หลายรายการ นอกจากนี้ คุณยังควบคุมจำนวนรายการในบรรทัดได้ด้วยการตั้งค่า maxItemsInEachRow หรือ maxItemsInEachColumn คุณมักจะใช้ FlowRow และ FlowColumn เพื่อสร้างเลย์เอาต์ที่ปรับเปลี่ยนตามอุปกรณ์ได้ โดยเนื้อหาจะไม่ถูกตัดออกหากรายการมีขนาดใหญ่เกินไปสำหรับมิติข้อมูลเดียว และการใช้ maxItemsInEach* ร่วมกับ Modifier.weight(weight) จะช่วยสร้างเลย์เอาต์ที่เติม/ขยายความกว้างของแถวหรือคอลัมน์เมื่อจำเป็นได้

ตัวอย่างที่พบบ่อยคือชิปหรือ UI การกรอง

ชิป 5 รายการใน FlowRow ซึ่งแสดงรายการที่ล้นไปยังบรรทัดถัดไปเมื่อไม่มีพื้นที่ว่างเหลืออยู่
รูปที่ 1 ตัวอย่างของ FlowRow

การใช้งานพื้นฐาน

หากต้องการใช้ FlowRow หรือ FlowColumn ให้สร้าง Composable เหล่านี้และวางรายการไว้ด้านในซึ่งควรเป็นไปตามขั้นตอนมาตรฐาน ดังนี้

@Composable
private fun FlowRowSimpleUsageExample() {
    FlowRow(modifier = Modifier.padding(8.dp)) {
        ChipItem("Price: High to Low")
        ChipItem("Avg rating: 4+")
        ChipItem("Free breakfast")
        ChipItem("Free cancellation")
        ChipItem("£50 pn")
    }
}

ข้อมูลโค้ดนี้จะแสดงผลใน UI ที่แสดงด้านบน โดยรายการจะเลื่อนไปยังแถวถัดไปโดยอัตโนมัติเมื่อไม่มีพื้นที่ในแถวแรกแล้ว

ฟีเจอร์ของเลย์เอาต์แบบไหล

เลย์เอาต์แบบโฟลว์มีฟีเจอร์และพร็อพเพอร์ตี้ต่อไปนี้ที่คุณใช้สร้างเลย์เอาต์ต่างๆ ในแอปได้

การจัดเรียงแกนหลัก: การจัดเรียงแนวนอนหรือแนวตั้ง

แกนหลักคือแกนที่ใช้จัดวางรายการ (เช่น ใน FlowRow รายการจะจัดเรียงในแนวนอน) พารามิเตอร์ horizontalArrangement ใน FlowRow จะควบคุมวิธีกระจายพื้นที่ว่างระหว่างรายการต่างๆ

ตารางต่อไปนี้แสดงตัวอย่างการตั้งค่า horizontalArrangement ในรายการสำหรับ FlowRow

ตั้งค่าการจัดเรียงแนวนอนใน FlowRow แล้ว

ผลลัพธ์

Arrangement.Start (Default)

รายการที่จัดเรียงตามจุดเริ่มต้น

Arrangement.SpaceBetween

การจัดเรียงรายการโดยมีช่องว่างระหว่างรายการ

Arrangement.Center

จัดเรียงสินค้าไว้ตรงกลาง

Arrangement.End

จัดเรียงรายการไว้ที่ส่วนท้าย

Arrangement.SpaceAround

รายการที่จัดเรียงโดยมีพื้นที่ว่างรอบๆ

Arrangement.spacedBy(8.dp)

รายการที่เว้นระยะห่างตาม dp ที่กำหนด

สำหรับ FlowColumn จะมีตัวเลือกที่คล้ายกันให้ใช้งานกับ verticalArrangement โดยมีค่าเริ่มต้นเป็น Arrangement.Top

การจัดเรียงแกนขวาง

แกนไขว้คือแกนในทิศทางตรงกันข้ามกับแกนหลัก เช่น ใน FlowRow แกนนี้คือแกนแนวตั้ง หากต้องการเปลี่ยนวิธีจัดเรียงเนื้อหาโดยรวมภายในคอนเทนเนอร์ในแกนขวาง ให้ใช้ verticalArrangement สำหรับ FlowRow และ horizontalArrangement สำหรับ FlowColumn

สำหรับ FlowRow ตารางต่อไปนี้แสดงตัวอย่างการตั้งค่า verticalArrangement ที่แตกต่างกันในรายการต่างๆ

ตั้งค่าการจัดเรียงแนวตั้งใน FlowRow

ผลลัพธ์

Arrangement.Top (Default)

การจัดเรียงคอนเทนเนอร์ด้านบน

Arrangement.Bottom

การจัดเรียงด้านล่างของคอนเทนเนอร์

Arrangement.Center

การจัดเรียงคอนเทนเนอร์ตรงกลาง

สำหรับ FlowColumn คุณสามารถใช้ตัวเลือกที่คล้ายกันได้ใน horizontalArrangement การจัดเรียงแกนขวางเริ่มต้นคือ Arrangement.Start

การจัดแนวรายการแต่ละรายการ

คุณอาจต้องการจัดตำแหน่งแต่ละรายการภายในแถวด้วยการจัดแนวที่แตกต่างกัน ซึ่งแตกต่างจาก verticalArrangement และ horizontalArrangement เนื่องจากจะจัดแนวรายการภายในบรรทัดปัจจุบัน คุณสามารถใช้ฟีเจอร์นี้กับ Modifier.align()

ตัวอย่างเช่น เมื่อรายการใน FlowRow มีความสูงต่างกัน แถวจะใช้ความสูงของรายการที่ใหญ่ที่สุดและใช้ Modifier.align(alignmentOption) กับรายการต่างๆ ดังนี้

ตั้งค่าการจัดแนวตั้งใน FlowRow

ผลลัพธ์

Alignment.Top (Default)

จัดรายการชิดด้านบน

Alignment.Bottom

จัดแนวรายการชิดด้านล่าง

Alignment.CenterVertically

จัดรายการให้อยู่ตรงกลาง

สำหรับ FlowColumn จะมีตัวเลือกที่คล้ายกัน การจัดแนวเริ่มต้นคือ Alignment.Start

จำนวนรายการสูงสุดในแถวหรือคอลัมน์

พารามิเตอร์ maxItemsInEachRow หรือ maxItemsInEachColumn จะกำหนดจำนวนรายการสูงสุดในแกนหลักที่อนุญาตใน 1 บรรทัดก่อนที่จะตัดขึ้นบรรทัดใหม่ ค่าเริ่มต้นคือ Int.MAX_INT ซึ่งจะอนุญาตให้มีรายการได้มากที่สุดเท่าที่จะเป็นไปได้ ตราบใดที่ขนาดของรายการพอดีกับบรรทัด

เช่น การตั้งค่า maxItemsInEachRow จะบังคับให้เลย์เอาต์เริ่มต้นมีเพียง 3 รายการเท่านั้น

ไม่ได้ตั้งค่าสูงสุด

maxItemsInEachRow = 3

ไม่ได้ตั้งค่าสูงสุดในแถวโฟลว์ จำนวนรายการสูงสุดที่ตั้งค่าไว้ในแถวโฟลว์

น้ำหนักของสินค้า

น้ำหนักจะขยายรายการตามปัจจัยและพื้นที่ว่างในบรรทัดที่วางรายการนั้น สิ่งสำคัญคือ FlowRow และ Row มีความแตกต่างกันในเรื่องวิธีใช้น้ำหนักเพื่อคำนวณความกว้างของรายการ สำหรับ Rows น้ำหนักจะอิงตามสินค้าทั้งหมดใน Row ใน FlowRow น้ำหนักจะอิงตามรายการในบรรทัดที่มีการวางรายการ ไม่ใช่รายการทั้งหมดในคอนเทนเนอร์ FlowRow

ตัวอย่างเช่น หากคุณมี 4 รายการที่อยู่บนเส้นเดียวกัน โดยแต่ละรายการมีน้ำหนักแตกต่างกันคือ 1f, 2f, 1f และ 3f น้ำหนักรวมจะเป็น 7f พื้นที่ที่เหลือในแถวหรือคอลัมน์จะถูกแบ่งด้วย 7f จากนั้นระบบจะคำนวณความกว้างของแต่ละรายการโดยใช้ weight * (remainingSpace / totalWeight)

คุณสามารถใช้ Modifier.weight และจำนวนรายการสูงสุดร่วมกับ FlowRow หรือ FlowColumn เพื่อสร้างเลย์เอาต์แบบตารางกริดได้ วิธีนี้มีประโยชน์สำหรับการสร้างเลย์เอาต์ที่ปรับเปลี่ยนตามอุปกรณ์ซึ่งจะปรับตามขนาดของอุปกรณ์

ตัวอย่างสิ่งที่คุณทำได้โดยใช้น้ำหนักมีดังนี้ ตัวอย่างหนึ่งคือตารางกริดที่รายการมีขนาดเท่ากัน ดังที่แสดงด้านล่าง

ตารางกริดที่สร้างขึ้นด้วยแถวแบบ Flow
รูปที่ 2 การใช้ FlowRow เพื่อสร้างตารางกริด

หากต้องการสร้างตารางกริดที่มีขนาดรายการเท่ากัน ให้ทำดังนี้

val rows = 3
val columns = 3
FlowRow(
    modifier = Modifier.padding(4.dp),
    horizontalArrangement = Arrangement.spacedBy(4.dp),
    maxItemsInEachRow = rows
) {
    val itemModifier = Modifier
        .padding(4.dp)
        .height(80.dp)
        .weight(1f)
        .clip(RoundedCornerShape(8.dp))
        .background(MaterialColors.Blue200)
    repeat(rows * columns) {
        Spacer(modifier = itemModifier)
    }
}

ที่สำคัญคือ หากคุณเพิ่มรายการอื่นและทำซ้ำ 10 ครั้งแทนที่จะเป็น 9 ครั้ง รายการสุดท้ายจะใช้ทั้งคอลัมน์สุดท้าย เนื่องจากน้ำหนักรวมของทั้งแถวคือ 1f ดังนี้

รายการสุดท้ายขนาดเต็มในตารางกริด
รูปที่ 3 การใช้ FlowRow เพื่อสร้างตารางกริดโดยให้รายการสุดท้ายมีความกว้างเต็ม

คุณสามารถรวมน้ำหนักกับModifiersอื่นๆ เช่น Modifier.width(exactDpAmount), Modifier.aspectRatio(aspectRatio) หรือ Modifier.fillMaxWidth(fraction) ได้ ตัวแก้ไขเหล่านี้ทั้งหมดทำงานร่วมกันเพื่อให้สามารถปรับขนาดรายการแบบตอบสนองภายใน FlowRow (หรือ FlowColumn) ได้

นอกจากนี้ คุณยังสร้างตารางกริดสลับกันของรายการขนาดต่างๆ ได้ โดยที่ 2 รายการใช้ความกว้างครึ่งหนึ่ง และ 1 รายการใช้ความกว้างเต็มของคอลัมน์ถัดไป ดังนี้

ตารางกริดสลับกันพร้อมแถวแบบไหล
รูปที่ 4 FlowRow ที่มีแถวขนาดสลับกัน

คุณสามารถทำได้โดยใช้โค้ดต่อไปนี้

FlowRow(
    modifier = Modifier.padding(4.dp),
    horizontalArrangement = Arrangement.spacedBy(4.dp),
    maxItemsInEachRow = 2
) {
    val itemModifier = Modifier
        .padding(4.dp)
        .height(80.dp)
        .clip(RoundedCornerShape(8.dp))
        .background(Color.Blue)
    repeat(6) { item ->
        // if the item is the third item, don't use weight modifier, but rather fillMaxWidth
        if ((item + 1) % 3 == 0) {
            Spacer(modifier = itemModifier.fillMaxWidth())
        } else {
            Spacer(modifier = itemModifier.weight(0.5f))
        }
    }
}

การปรับขนาดเศษส่วน

คุณสามารถใช้ Modifier.fillMaxWidth(fraction) เพื่อระบุขนาดของคอนเทนเนอร์ที่รายการควรใช้ ซึ่งแตกต่างจากวิธีการทำงานของ Modifier.fillMaxWidth(fraction) เมื่อใช้กับ Row หรือ Column ตรงที่รายการ Row/Column จะใช้พื้นที่เป็นเปอร์เซ็นต์ของความกว้างที่เหลืออยู่ ไม่ใช่ความกว้างของคอนเทนเนอร์ทั้งหมด

ตัวอย่างเช่น โค้ดต่อไปนี้จะให้ผลลัพธ์ที่แตกต่างกันเมื่อใช้ FlowRow เทียบกับ Row

FlowRow(
    modifier = Modifier.padding(4.dp),
    horizontalArrangement = Arrangement.spacedBy(4.dp),
    maxItemsInEachRow = 3
) {
    val itemModifier = Modifier
        .clip(RoundedCornerShape(8.dp))
    Box(
        modifier = itemModifier
            .height(200.dp)
            .width(60.dp)
            .background(Color.Red)
    )
    Box(
        modifier = itemModifier
            .height(200.dp)
            .fillMaxWidth(0.7f)
            .background(Color.Blue)
    )
    Box(
        modifier = itemModifier
            .height(200.dp)
            .weight(1f)
            .background(Color.Magenta)
    )
}

FlowRow: รายการตรงกลางที่มีเศษส่วน 0.7 ของความกว้างคอนเทนเนอร์ทั้งหมด

ความกว้างเศษส่วนที่มีแถวแบบไหล

Row: รายการตรงกลางใช้พื้นที่ 0.7 เปอร์เซ็นต์ของความกว้าง Row ที่เหลือ

ความกว้างเศษส่วนพร้อมแถว

fillMaxColumnWidth() และ fillMaxRowHeight()

การใช้ Modifier.fillMaxColumnWidth() หรือ Modifier.fillMaxRowHeight() กับรายการภายใน FlowColumn หรือ FlowRow จะช่วยให้มั่นใจได้ว่ารายการในคอลัมน์หรือแถวเดียวกันจะมีความกว้างหรือความสูงเท่ากับรายการที่ใหญ่ที่สุดในคอลัมน์/แถว

ตัวอย่างเช่น ตัวอย่างนี้ใช้ FlowColumn เพื่อแสดงรายการขนมหวานของ Android คุณจะเห็นความแตกต่างของความกว้างของแต่ละรายการเมื่อใช้ Modifier.fillMaxColumnWidth() กับรายการเทียบกับเมื่อไม่ได้ใช้และรายการตัดขึ้นบรรทัดใหม่

FlowColumn(
    Modifier
        .padding(20.dp)
        .fillMaxHeight()
        .fillMaxWidth(),
    horizontalArrangement = Arrangement.spacedBy(8.dp),
    verticalArrangement = Arrangement.spacedBy(8.dp),
    maxItemsInEachColumn = 5,
) {
    repeat(listDesserts.size) {
        Box(
            Modifier
                .fillMaxColumnWidth()
                .border(1.dp, Color.DarkGray, RoundedCornerShape(8.dp))
                .padding(8.dp)
        ) {

            Text(
                text = listDesserts[it],
                fontSize = 18.sp,
                modifier = Modifier.padding(3.dp)
            )
        }
    }
}

ใช้ Modifier.fillMaxColumnWidth() กับแต่ละรายการแล้ว

fillMaxColumnWidth

ไม่ได้ตั้งค่าการเปลี่ยนแปลงความกว้าง (รายการที่ตัดขึ้นบรรทัดใหม่)

ไม่ได้ตั้งค่าความกว้างคอลัมน์สูงสุดแบบเติมเต็ม