유스의 개발 일지
[Android/Kotlin]Compose Layout(2) 본문
Adaptive Layout (적응형 레이아웃)
- List-Detail
- 설명이나 추가 정보를 포함한 항목을 리스트 패널과 디테인 패널, 두 개 패널로 나눠 보여줍니다. 확장된 화면에서는 목록과 상세 정보를 동시에 보여주고, 중간 또는 소형 화면에서는 상호작용에 따라 리스트 패널 또는 디테인 패널만 표시합니다. ( 메시징 앱, 연락처 관리자, 파일 탐색기 등 )
- 피드 레이아웃 (Feed Layout)
- 피드 레이아웃은 동일한 콘텐츠 요소를 빠르게 볼 수 있도록 그리드로 배치합니다. 화면 크기에 맞춰 단일 열부터 다중 열의 스크롤 가능한 피드를 지원합니다. ( 뉴스 앱, 소셜 미디어 앱 등 )
- 지원창 레이아웃 (Supporting Pane Layout)
- 지원창 레이아웃은 주 패널 (Main Pane) 와 보조 패널 (Supporting Pane) 를 두 개의 영역으로 나눕니다. 확장된 화면에서는 두 콘텐츠를 나란히 배치하고, 중간 및 소형 화면에서는 보조 콘텐츠를 하단 시트로 표시할 수 있습니다. (생산성 앱 - 문서와 검토 코멘트, 미디어 앱 - 동영상과 관련 비디오 목록 등 )
Compose 앱의 주요 컴포저블은 기본적으로 화면 전체를 채우도록 설계되며, 이 경우 화면 크기에 따라 UI Layout을 유연하게 변경하여 대형 화면에서도 최적화된 경험을 제공하는 것이 좋습니다.
- 앱 수준 컴포저블 : 앱에 지정된 모든 공간을 차지하고 다른 모든 컴포저블을 포함하는 단일 루트 컴포저블입니다.
- 화면 수준 컴포저블 : 앱의 모든 공간을 차지하는 앱 수준 컴포저블 내에 포함된 컴포저블입니다. 각 화면 수준 컴포저블은 앱을 탐색할 때 일반적으로 특정 대상을 나타냅니다.
- 개별 컴포저블 : 앱 수준 또는 화면 수준 컴포저블이 아닌 모든 컴포저블 이러한 요소는 개별 요소, 재사용 가능한 콘텐츠 그룹, 화면 수준 컴포저블 내에서 호스팅되는 컴포저블일 수 있습니다.
BoxWithConstraints ( 사용 가능한 공간에 따라 다양한 컴포저블을 호출하는 데 사용할 수 있는 측정 제약 조건을 제공합니다. ) 레이아웃을 변경해도 상태를 유지할 수 있습니다.
일부 크기에서 사용되지 않을 수도 있는 정보를 호이스팅하여 Layout 크기가 변경될 때 사용자의 상태를 유지할 수 있습니다.
예를 들어 크기 조절로 인해 Layout이 설명 숨기기와 설명 표시하기 간에 전환될 때 사용자 상태가 유지되도록 showMore Boolean 플래그를 호이스팅할 수 있습니다.
@Composable
fun Card(
imageUrl: String,
title: String,
description: String
) {
var showMore by remember { mutableStateOf(false) }
BoxWithConstraints {
if (maxWidth < 400.dp) {
Column {
Image(imageUrl)
Title(title)
}
} else {
Row {
Column {
Title(title)
Description(
description = description,
showMore = showMore,
onShowMoreToggled = { newValue ->
showMore = newValue
}
)
}
Image(imageUrl)
}
}
}
}
Compose의 선언형 패러다임과 윈도우 크기 클래스 로직을 활용하여 화면 크기에 따라 적절한 레이아웃을 구현할 수 있습니다.
@Composable
fun MyApp(
windowSizeClass: WindowSizeClass = currentWindowAdaptiveInfo().windowSizeClass
) {
// Perform logic on the size class to decide whether to show the top app bar.
val showTopAppBar = windowSizeClass.windowHeightSizeClass != WindowHeightSizeClass.COMPACT
// MyScreen knows nothing about window sizes, and performs logic based on a Boolean flag.
MyScreen(
showTopAppBar = showTopAppBar,
/* ... */
)
}
- Compact Width : 가로 < 600dp, 대부분 스마트폰 세로 화면에 해당
- Compact Height : 높이 < 480dp, 대부분 스마트폰 가로 화면에 해당
- Medium Width : 600dp ≤ 가로 < 840dp, 태블릿 세로 화면 및 대부분의 대형 폴더블 기기 화면
- Medium Height : 480dp ≤ 높이 < 900dp, 태블릿 가로 화면 및 대부분의 스마트폰 세로 화면
- Expanded Width : 가로 ≥ 840dp, 태블릿 가로 화면 및 대부분의 대형 폴더블 기기 가로 화면
- Expanded Height : 높이 ≥ 900dp, 세로 화면 태블릿
* Window Size Class 특징은 기기의 화면 크기와 관계없이 앱에 할당된 화면 크기로 분류된다. 기기의 화면 크기가 동일하더라도, 분할 화면 모드, ChromeOS의 데스크톱 스타일 창, 폴더블 디바이스의 접힘/펼침 등으로 인해 앱에 할당된 화면 크기가 달라질 수 있습니다.
Compose의 ConstraintLayout
ConstraintLayout은 화면에 다른 컴포저블을 기준으로 컴포저블을 배치할 수 있는 레이아웃입니다. 여러 중첩된 Row, Column, Box, 맞춤 레이아웃 요소 대신 사용할 수 있습니다. ConstraintLayout은 더 복잡한 정렬 요구사항이 있는 더 큰 레이아웃을 구현할 때 유용합니다.
다음 시나리오일때 ConstraintLayout을 사용하는 것이 좋다.
- 코드 가독성 개선을 위해 화면에 요소를 배치하는 여러 Column 및 Row를 중첩하지 않습니다.
- 다른 컴포저블을 기준으로 컴포저블을 배치하거나 가이드라인, 배리어, 체인을 기반으로 컴포저블을 배치합니다.
implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1"
@Composable
fun DecoupledConstraintLayout() {
BoxWithConstraints {
val constraints = if (minWidth < 600.dp) {
decoupledConstraints(margin = 16.dp) // Portrait constraints
} else {
decoupledConstraints(margin = 32.dp) // Landscape constraints
}
ConstraintLayout(constraints) {
Button(
onClick = { /* Do something */ },
modifier = Modifier.layoutId("button")
) {
Text("Button")
}
Text("Text", Modifier.layoutId("text"))
}
}
}
private fun decoupledConstraints(margin: Dp): ConstraintSet {
return ConstraintSet {
val button = createRefFor("button")
val text = createRefFor("text")
constrain(button) {
top.linkTo(parent.top, margin = margin)
}
constrain(text) {
top.linkTo(button.bottom, margin)
}
}
}
배리어 ( Barrier ) : 레이아웃에서 서로 겹치지 않도록 특정 UI 요소들 사이에 공간을 설정하는 역할을 합니다.
ConstraintLayout {
val (text1, text2, button) = createRefs()
val barrier = createEndBarrier(text1, text2)
Text(
text = "짧은 텍스트",
Modifier.constrainAs(text1) {
start.linkTo(parent.start)
top.linkTo(parent.top)
}
)
Text(
text = "좀 더 긴 텍스트",
Modifier.constrainAs(text2) {
start.linkTo(parent.start)
top.linkTo(text1.bottom)
}
)
Button(
onClick = { /* 클릭 동작 */ },
Modifier.constrainAs(button) {
start.linkTo(barrier) // 배리어의 끝에 맞춰 버튼 위치 설정
top.linkTo(text2.bottom)
}
)
}
체인 ( Chain ) : ConstraintLayout을 사용하여 여러 UI 요소들을 하나의 축에 따라 정렬하고, 간격이나 분배 방식을 제어하는 기능입니다.
ConstraintLayout(modifier = Modifier.fillMaxSize()) {
val (button1, button2, button3) = createRefs()
Button(
onClick = { /* 클릭 동작 */ },
modifier = Modifier.constrainAs(button1) {
top.linkTo(parent.top, margin = 16.dp)
}
) {
Text("Button 1")
}
Button(
onClick = { /* 클릭 동작 */ },
modifier = Modifier.constrainAs(button2) {
top.linkTo(parent.top, margin = 16.dp)
}
) {
Text("Button 2")
}
Button(
onClick = { /* 클릭 동작 */ },
modifier = Modifier.constrainAs(button3) {
top.linkTo(parent.top, margin = 16.dp)
}
) {
Text("Button 3")
}
createHorizontalChain(button1, button2, button3, chainStyle = ChainStyle.Spread)
}
'Android' 카테고리의 다른 글
[Android/Kotlin] Compose Lazy lists (2) | 2024.11.13 |
---|---|
[Android/Kotlin]Compose Dialog (0) | 2024.11.12 |
[Android/Kotlin] Compose App Bar (0) | 2024.11.10 |
[Android/Kotlin] Compose Layout(1) (5) | 2024.11.09 |
[Android/Kotlin] Compose Layout (2) | 2024.11.08 |