تنسيقات التدفق في "إنشاء"

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

يؤدي هذا المقتطف إلى ظهور واجهة المستخدم الموضّحة أعلاه، مع تدفق العناصر تلقائيًا إلى الصف التالي عندما لا تتوفّر مساحة في الصف الأول.

ميزات تنسيق التدفق

تتضمّن تنسيقات "التنقّل" الميزات والخصائص التالية التي يمكنك استخدامها ل إنشاء تنسيقات مختلفة في تطبيقك.

ترتيب المحور الرئيسي: ترتيب أفقي أو عمودي

المحور الرئيسي هو المحور الذي يتم ترتيب العناصر عليه (على سبيل المثال، في 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 الحد الأقصى للعناصر في المحور الرئيسي للسماح بها في سطر واحد قبل الانتقال إلى السطر التالي. القيمة التلقائية هي Int.MAX_INT، وهي تسمح بأكبر عدد ممكن من العناصر، ما دامت أحجامها تسمح بوضعها في السطر.

على سبيل المثال، يؤدي ضبط maxItemsInEachRow إلى فرض اتّباع التنسيق الأولي الذي يتضمّن 3 عناصر فقط:

لم يتم ضبط حدّ أقصى

maxItemsInEachRow = 3

لم يتم ضبط حدّ أقصى في صفّ "مسار الإحالة الناجحة" الحد الأقصى للعناصر التي تم ضبطها في صفّ العملية

عناصر مسار التحميل الكسول

ContextualFlowRow وContextualFlowColumn هما علامتان متخصصتان لهما FlowRow وFlowColumn، تتيحان لك تحميل محتوى الصف أو العمود في العرض بدون تحميله بالكامل. وتوفّر هذه السمات أيضًا معلومات حول موضع العناصر (الفهرس ورقم الصف والحجم المتاح)، مثل ما إذا كان العنصر في الصف الأول. يكون ذلك مفيدًا لمجموعات البيانات الكبيرة وإذا كنت بحاجة إلى معلومات سياقية عن عنصر معيّن.

تحدّ المَعلمة maxLines من عدد الصفوف المعروضة، وتحدّد المَعلمة overflow ما يجب عرضه عند الوصول إلى عدد كبير جدًا من العناصر ، ما يتيح لك تحديد expandIndicator أو collapseIndicator مخصّصَين.

على سبيل المثال، لعرض زر "+ (عدد العناصر المتبقية)" أو "عرض أقل":

val totalCount = 40
var maxLines by remember {
    mutableStateOf(2)
}

val moreOrCollapseIndicator = @Composable { scope: ContextualFlowRowOverflowScope ->
    val remainingItems = totalCount - scope.shownItemCount
    ChipItem(if (remainingItems == 0) "Less" else "+$remainingItems", onClick = {
        if (remainingItems == 0) {
            maxLines = 2
        } else {
            maxLines += 5
        }
    })
}
ContextualFlowRow(
    modifier = Modifier
        .safeDrawingPadding()
        .fillMaxWidth(1f)
        .padding(16.dp)
        .wrapContentHeight(align = Alignment.Top)
        .verticalScroll(rememberScrollState()),
    verticalArrangement = Arrangement.spacedBy(4.dp),
    horizontalArrangement = Arrangement.spacedBy(8.dp),
    maxLines = maxLines,
    overflow = ContextualFlowRowOverflow.expandOrCollapseIndicator(
        minRowsToShowCollapse = 4,
        expandIndicator = moreOrCollapseIndicator,
        collapseIndicator = moreOrCollapseIndicator
    ),
    itemCount = totalCount
) { index ->
    ChipItem("Item $index")
}

مثال على صفوف مسارات المستخدمين السياقية
الشكل 2. مثال على ContextualFlowRow

أوزان السلع

يؤدي الوزن إلى زيادة حجم العنصر استنادًا إلى عامله والمساحة المتوفّرة في السطر الذي تم وضعه فيه. من المهم معرفة أنّ هناك فرقًا بين FlowRow وRow في كيفية استخدام الأوزان لحساب عرض السلعة. بالنسبة إلى Rows، يستند الوزن إلى جميع السلع في Row. في سمة FlowRow، يستند الوزن إلى العناصر في الصف الذي يتم وضع العنصر فيه، وليس إلى جميع العناصر في حاوية FlowRow.

على سبيل المثال، إذا كان لديك 4 سلع تقع جميعها على خط واحد، ولكل منها كثافة مختلفة تبلغ 1f, 2f, 1f و3f، يكون إجمالي الكثافة 7f. سيتم تقسيم المساحة المتبقية في صف أو عمود على 7f. بعد ذلك، سيتم حساب عرض كل عنصر باستخدام: weight * (remainingSpace / totalWeight).

يمكنك استخدام مجموعة من Modifier.weight وعدد العناصر الأقصى مع FlowRow أو FlowColumn لإنشاء تنسيق شبيه بالشبكة. يُعدّ هذا النهج مفيدًا لإنشاء تصاميم متعالِمة تتكيّف مع حجم جهازك.

في ما يلي بعض الأمثلة المختلفة على ما يمكنك تحقيقه باستخدام الأوزان. أحد مثالي ها هو شبكة تكون فيها العناصر متساوية الحجم، كما هو موضّح أدناه:

شبكة تم إنشاؤها باستخدام صف تدفق
الشكل 3. استخدام 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:

عرض العنصر الأخير بالحجم الكامل في الشبكة
الشكل 4. استخدام FlowRow لإنشاء شبكة يكون فيها العنصر الأخير يشغل العرض الكامل

يمكنك دمج الأوزان مع Modifiers أخرى، مثل Modifier.width(exactDpAmount), Modifier.aspectRatio(aspectRatio) أو Modifier.fillMaxWidth(fraction). تعمل جميع هذه المُعدِّلات معًا لسماح بتغيير حجم العناصر داخل FlowRow (أو FlowColumn) استجابةً للشاشة.

يمكنك أيضًا إنشاء شبكة بديلة بأحجام عناصر مختلفة، حيث يشغل عنصران نصف العرض كلّ منهما، ويشغل عنصر واحد العرض الكامل للعمود التالي:

شبكة بديلة مع صفّ تدفق
الشكل 5. 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

لم يتم ضبط أي تغييرات على العرض (العناصر التي يتم لفها)

لم يتم ضبط الحد الأقصى لعرض العمود المعبّأ