Макеты потоков в Compose

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)

Элементы, расположенные на определенном расстоянии друг от друга

Для 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

Максимальное значение для строки потока не установленоМаксимальное количество элементов, установленных в строке потока

Вес товара

Weight увеличивает элемент на основе его коэффициента и доступного пространства на строке, в которую он был помещен. Важно отметить, что существует разница между FlowRow и Row в том, как веса используются для расчета ширины элемента. Для Rows вес основан на всех элементах в Row . С FlowRow вес основан на элементах в строке, в которую помещен элемент , а не на всех элементах в контейнере FlowRow .

Например, если у вас есть 4 элемента, которые все попадают в линию, каждый с разным весом 1f, 2f, 1f и 3f , общий вес составляет 7f . Оставшееся пространство в строке или столбце будет разделено на 7f . Затем ширина каждого элемента будет рассчитана с помощью: weight * (remainingSpace / totalWeight) .

Вы можете использовать комбинацию Modifier.weight и max элементов с 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

Изменения ширины не установлены (оборачивание элементов)

Максимальная ширина столбца не задана.
{% дословно %} {% endverbatim %} {% дословно %} {% endverbatim %}