Макеты потоков в 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")
    }
}

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

Особенности компоновки потока

Макеты 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 .

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

В строке потока не задано максимальное значение.Максимальное количество элементов, заданное в строке потока.

Вес предметов

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

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

Для создания сетчатой ​​структуры можно использовать комбинацию параметров Modifier.weight и max items с FlowRow или FlowColumn . Такой подход полезен для создания адаптивных макетов, которые подстраиваются под размер экрана устройства.

Существует несколько примеров того, чего можно добиться с помощью весов. Один из примеров — сетка, где элементы имеют одинаковый размер, как показано ниже:

Сетка создана с помощью flow row
Рисунок 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

Ширина не изменена (для упаковки товаров).

Запрещено заполнять, задана максимальная ширина столбца
{% verbatim %} {% endverbatim %} {% verbatim %} {% endverbatim %}