פריסות זרימה ב'כתיבה'

FlowRow ו-FlowColumn הם קומפוזיציות שדומות ל-Row ול-Column, אבל ההבדל הוא שהפריטים עוברים לשורה הבאה כשנגמר המקום בקונטיינר. הפעולה הזו יוצרת כמה שורות או עמודות. אפשר גם לשלוט במספר הפריטים בשורה באמצעות ההגדרות maxItemsInEachRow או maxItemsInEachColumn. לרוב אפשר להשתמש ב-FlowRow וב-FlowColumn כדי ליצור פריסות דינמיות – התוכן לא ייחתך אם הפריטים גדולים מדי עבור מימד מסוים, ואפשר להשתמש בשילוב של maxItemsInEach* עם Modifier.weight(weight) כדי ליצור פריסות שמתרחבות לרוחב של שורה או עמודה כשצריך.

דוגמה טיפוסית היא ממשק משתמש של צ'יפ או סינון:

‫5 צ'יפים ב-FlowRow, שמוצגים בשורה הבאה כשהם לא נכנסים בשורה הנוכחית.
איור 1. דוגמה לערך FlowRow

שימוש בסיסי

כדי להשתמש ב-FlowRow או ב-FlowColumn, צריך ליצור את הפונקציות האלה שניתנות להרכבה ולמקם בתוכן את הפריטים שצריכים לפעול לפי התהליך הרגיל:

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

הקוד הזה יוצר את ממשק המשתמש שמוצג למעלה, שבו הפריטים עוברים אוטומטית לשורה הבאה כשאין יותר מקום בשורה הראשונה.

תכונות של פריסת זרימה

לפריסות מסוג Flow יש את התכונות והמאפיינים הבאים שבהם אפשר להשתמש כדי ליצור פריסות שונות באפליקציה.

הסידור של הציר הראשי: סידור אופקי או אנכי

הציר הראשי הוא הציר שבו הפריטים מסודרים (לדוגמה, ב-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.

בטבלה הבאה מוצגות דוגמאות להגדרת ערכים שונים של verticalArrangement לפריטים ב-FlowRow:

הסידור האנכי מוגדר ב-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 מגדירים את מספר הפריטים המקסימלי בציר הראשי שאפשר להציג בשורה אחת לפני שהם עוברים לשורה הבאה. ברירת המחדל היא 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 כדי ליצור פריסה דמוית רשת. הגישה הזו שימושית ליצירת פריסות רספונסיביות שמותאמות לגודל המכשיר.

יש כמה דוגמאות שונות למה שאפשר להשיג באמצעות משקלים. דוגמה אחת היא רשת שבה הפריטים הם בגודל שווה, כמו בדוגמה הבאה:

רשת שנוצרה עם שורת זרימה
איור 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).

אפשר גם ליצור רשת לסירוגין של פריטים בגדלים שונים, שבה שני פריטים תופסים כל אחד חצי מהרוחב, ופריט אחד תופס את הרוחב המלא של העמודה הבאה:

רשת לסירוגין עם שורה זורמת
איור 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

לא הוגדרו שינויים ברוחב (פריטים שמוצגים בשורות)

לא הוגדר רוחב עמודה מקסימלי למילוי