Compose의 Flow 레이아웃

FlowRowFlowColumn RowColumn와 비슷하지만 항목이 다른 컴포저블 컨테이너의 공간이 부족해지면 다음 줄로 전달됩니다. 이렇게 하면 여러 행이나 열을 생성할 수 있습니다. 광고 항목에 있는 항목의 개수는 maxItemsInEachRow 또는 maxItemsInEachColumn를 설정하면 됩니다. 보통 FlowRowFlowColumn: 반응형 레이아웃 빌드 - 콘텐츠가 잘리지 않음 항목이 한 측정기준에 비해 너무 큰 경우 할인되며 maxItemsInEach*Modifier.weight(weight)와 함께 사용하면 필요한 경우 행 또는 열의 너비를 채우거나 확장합니다.

일반적인 예는 칩 또는 필터링 UI입니다.

FlowRow에 5개의 칩이 있으며,
여유 공간이 확보됩니다.
그림 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")
    }
}

이 스니펫은 위에 표시된 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

Container 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

흐름 행에 최댓값이 설정되지 않음 흐름 행에 설정된 최대 항목 수

지연 로드 흐름 항목

ContextualFlowRowContextualFlowColumn는 콘텐츠를 지연 로드할 수 있는 FlowRowFlowColumn의 버전 흐름 행 또는 열에 표시됩니다. 또한 항목 위치에 대한 정보도 제공합니다. (색인, 행 번호, 사용 가능한 크기) 항목이 첫 번째 행 이 기능은 대규모 데이터 세트가 있거나 컨텍스트 정보가 필요한 경우에 유용합니다. 자세히 알아볼 수 있습니다

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 예시

상품 가중치

가중치는 요소 및 선의 사용 가능한 공간에 따라 항목을 늘립니다. 배치되었습니다. FlowRowRow에는 차이가 있습니다. 가중치를 사용하여 항목의 너비를 계산하는 방법을 보여줍니다. Rows의 경우, 가중치 Row모든 항목을 기반으로 합니다. FlowRow를 사용하는 경우 가중치는 항목이 배치된 광고 항목(광고 항목의 모든 항목이 아님) FlowRow 컨테이너

예를 들어 한 행에 4개의 항목이 있고 각 항목이 서로 다른 1f, 2f, 1f3f의 가중치인 경우 총 가중치는 7f입니다. 남은 공간 행 또는 열의 값은 7f로 나눕니다. 그런 다음 각 항목의 너비는 weight * (remainingSpace / totalWeight)을(를) 사용하여 계산됩니다.

FlowRow와 함께 Modifier.weight 및 최대 항목 조합을 사용할 수 있습니다. FlowColumn: 그리드와 같은 레이아웃을 만듭니다. 이 방법은 기기의 크기에 맞게 조정되는 반응형 레이아웃을 제공합니다.

가중치를 사용하여 무엇을 달성할 수 있는지에 대한 몇 가지 다른 예가 있습니다. 1개 예는 아래와 같이 항목의 크기가 동일한 그리드입니다.

흐름 행으로 그리드 생성됨
그림 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)
    }
}

중요한 점은 다른 항목을 추가하고 9번이 아닌 10번 반복하면 마지막 항목이 마지막 열 전체를 차지하며, 이는 전체 행의 총 가중치입니다. 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: 중간 항목이 나머지 Row 너비의 0.7%를 차지합니다.

행이 있는 소수 너비

fillMaxColumnWidth()fillMaxRowHeight()

Modifier.fillMaxColumnWidth() 또는 FlowColumn 또는 FlowRow 내 항목에 Modifier.fillMaxRowHeight() 추가 같은 열이나 행에 있는 항목이 열/행에서 가장 큰 항목입니다.

예를 들어 이 예에서는 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

너비 변경이 설정되지 않음 (래핑 항목)

채우기 최대 열 너비가 설정되지 않았습니다.