-
Jetpack compose 레이아웃 - ②Android 2023. 5. 12. 16:58
들어가기 전에
Compose 에서 제공하는 Material 레이아웃의 대해 알아보자.
Material 구성요소 및 레이아웃
Jetpack Compose 는 디지털 인터페이스를 만들기 위한 포괄적인 디자인 시스템인 Material Design 구현을 제공한다.
Material 구성요소(버튼, 카드, 스위치 등) 및 레이아웃(예: Scaffold)은 구성 가능한 함수로 사용할 수 있다.
Material 구성요소는 사용자 인터페이스를 만드는 대화형 구성요소이다. Compose 는 이러한 여러 구성요소를 바로 제공한다.
Material 구성요소는 앱의 MaterialTheme 에서 제공하는 값을 사용한다.
@Composable fun MyApp() { MaterialTheme { // Material Components like Button, Card, Switch, etc. } }Material 테마 설정에 관한 자세한 내용은 여기를 참고하자.
콘텐츠 슬롯
내부 콘텐츠(텍스트 라벨, 아이콘 등)를 지원하는 Material 구성요소는 구성 가능한 콘텐츠를 허용하는 일반 람다인 '슬롯'과 함께 공개 상수(예: 크기, 패딩)를 제공하여 Material 사양과 일치하도록 내부 콘텐츠 배치를 지원하는 경향이 있다.
Button 예시를 보자.
Button( onClick = { /* ... */ }, // Uses ButtonDefaults.ContentPadding by default contentPadding = PaddingValues( start = 20.dp, top = 12.dp, end = 20.dp, bottom = 12.dp ) ) { // Inner content including an icon and a text label Icon( Icons.Filled.Favorite, contentDescription = "Favorite", modifier = Modifier.size(ButtonDefaults.IconSize) ) Spacer(Modifier.size(ButtonDefaults.IconSpacing)) Text("Like") }Button 에는 일반 content 후행 람다 슬롯이 있고 이 슬롯은 RowScope 를 사용하여 행에서 콘텐츠 컴포저블을 배치한다.
내부 콘텐츠에 패딩을 적용하는 contentPadding 매개변수도 있다. ButtonDefaults 를 통해 제공된 상수나 맞춤 값을 사용할 수 있다.
무슨 소리인가 싶다..... 다시 설명하자면 이런것이다.
@Composable fun Button( onClick: () -> Unit, modifier: Modifier = Modifier, enabled: Boolean = true, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, elevation: ButtonElevation? = ButtonDefaults.elevation(), shape: Shape = MaterialTheme.shapes.small, border: BorderStroke? = null, colors: ButtonColors = ButtonDefaults.buttonColors(), contentPadding: PaddingValues = ButtonDefaults.ContentPadding, content: @Composable RowScope.() -> Unit ): Unitcontent 후행 람다 슬롯이 있다 ☞ @Composable RowScope.() → Unit 형태를 제공한다는 뜻이며 이것이 하나의 슬롯이된다. 이 슬롯은 RowScope 인터페이스를 사용해 행에서 콘텐츠를 배치한다는 뜻이다.
내부 콘텐츠에 패딩을 적용하는 contentPadding 매개변수도 있다 ☞ 말 그대로 매개변수로 contentPadding 제공하는데 ButtonDefaults.contentPadding 을 통해 미리 정해놓은 상수 값을 사용할 수 있고 상/하/좌/우 값을 커스텀 할 수 있다는 뜻이다.
ExtendedFloatingActionButton 예시를 보자.
ExtendedFloatingActionButton( onClick = { /* ... */ }, icon = { Icon( Icons.Filled.Favorite, contentDescription = "Favorite" ) }, text = { Text("Like") } )일반 content 람다가 아닌 ExtendedFloatingActionButton 에는 icon 과 text 라벨용 슬롯 2개가 있다. 각 슬롯은 구성 가능한 일반 콘텐츠를 지원하지만 구성요소는 이러한 내부 콘텐츠 배치 방식에 관해 독단적이다. 내부적으로 패딩과 정렬, 크기를 처리한다.
@Composable fun ExtendedFloatingActionButton( text: @Composable () -> Unit, onClick: () -> Unit, modifier: Modifier = Modifier, icon: (@Composable () -> Unit)? = null, interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }, shape: Shape = MaterialTheme.shapes.small.copy(CornerSize(percent = 50)), backgroundColor: Color = MaterialTheme.colors.secondary, contentColor: Color = contentColorFor(backgroundColor), elevation: FloatingActionButtonElevation = FloatingActionButtonDefaults.elevation() ): Unit슬롯에 대해서 정리한 후 이 구문을 보니 그나마 조금 이해가 된다.
icon 과 text 라벨용 슬롯 2개가 있다 ☞ @Composable () → Unit 형태를 제공하는게 2개 보이니 슬롯 2개가 있다는 뜻이다.
각 슬롯은 일반 콘텐츠를 지원하지만 구성요소는 이러한 내부 콘텐츠 배치 방식에 관해 독단적 ☞ icon 과 text 요소는 슬롯형태로 또 다른 구성 요소를 구성할 수 있다는 뜻이며 이렇게 구성된 콘텐츠 배치 방식은 상위 구성요소와는 연관되어 움직이지 않고 독단적으로 배치된다는 뜻이다.
말을 아주 쉽지 않게 설명 되어있어 재해석해보았다.
Scaffold
compose 는 Material 구성요소를 일반 화면 패턴으로 결합하는 편리한 레이아웃을 제공한다. Scaffold 와 같은 컴포저블은 다양한 구성요소와 기타 화면 요소를 위한 슬롯을 제공한다.
✅ 여기서 잠깐
Slot 의 사전적 정의는 '무엇인가 집어넣게 만든 가느다란 구멍, 자리, 틈" 을 뜻하며 쉽게 말해 무언가 넣을 수 있는 빈 틈을 말한다.화면 콘텐츠
Scaffold 에는 일반 content 후행 람다 슬롯이 있다. 람다는 콘텐츠 루트에 적용해야 하는 PaddingValues 인스턴스를 수신(예: Modifier.padding 을 통해) 하여 상단과 하단 막대가 있으면 이를 오프셋한다.
@Composable fun Scaffold( modifier: Modifier = Modifier, scaffoldState: ScaffoldState = rememberScaffoldState(), topBar: @Composable () -> Unit = {}, bottomBar: @Composable () -> Unit = {}, snackbarHost: @Composable (SnackbarHostState) -> Unit = { SnackbarHost(it) }, floatingActionButton: @Composable () -> Unit = {}, floatingActionButtonPosition: FabPosition = FabPosition.End, isFloatingActionButtonDocked: Boolean = false, drawerContent: (@Composable ColumnScope.() -> Unit)? = null, drawerGesturesEnabled: Boolean = true, drawerShape: Shape = MaterialTheme.shapes.large, drawerElevation: Dp = DrawerDefaults.Elevation, drawerBackgroundColor: Color = MaterialTheme.colors.surface, drawerContentColor: Color = contentColorFor(drawerBackgroundColor), drawerScrimColor: Color = DrawerDefaults.scrimColor, backgroundColor: Color = MaterialTheme.colors.background, contentColor: Color = contentColorFor(backgroundColor), content: @Composable (PaddingValues) -> Unit ): Unit위 내용도 한번에 이해하기 쉽지 않다. 쉽게 말해 화면 콘텐츠 꾸밀 때 Scaffold 를 제공하고 이 함수는 매개변수도 받을 수 있고 슬롯도 제공하고 있어 상단과 하단에 bar 가 있으면 요소 간의 거리를 루트 콘텐츠와 수신해 배치한다.
아래 예시를 보며 이해해보자.
앱 바
Scaffold 는 상단 앱 바 and 하단 앱 바 슬롯을 제공한다.
Scaffold( topBar = { TopAppBar { /* Top app bar content */ } } ) { // Screen content }
Scaffold( bottomBar = { BottomAppBar { /* Bottom app bar content */ } } ) { // Screen content }
이러한 슬롯은 BottomNavigation 과 같은 다른 Material 구성요소에 사용할 수 있다.
플로팅 작업 버튼
Scaffold 는 floatingActionButton 슬롯과 FloatingActionButton 을 사용할 수 있다.
Scaffold( floatingActionButton = { FloatingActionButton(onClick = { /* ... */ }) { /* FAB content */ } } ) { // Screen content }새 이메일 작성 플로팅 작업 버튼

두 개(현재 위치 찾기, 길 찾기)의 중요한 기능을 제공하는 플로팅 작업 버튼

스낵바
Scaffold 는 스낵바를 표시하는 방법을 제공한다.
SnackbarHostState 속성이 포함된 ScaffoldState 를 통해 제공된다. rememberScaffoldState 를 사용하여 scaffoldState 매개변수로 Scaffold 에 전달해야 하는 ScaffoldState 인스턴스를 만들 수 있다.
SnackbarHostState 를 통해 showSnackbar 함수에 액세스할 수 있다. 이 함수는 CoroutineScope 가 필요하고 UI 이벤트에 대한 응답으로 호출되어 Scaffold 내의 Snackbar 를 표시할 수 있다.
val scaffoldState = rememberScaffoldState() val scope = rememberCoroutineScope() Scaffold( scaffoldState = scaffoldState, floatingActionButton = { ExtendedFloatingActionButton( text = { Text("Show snackbar") }, onClick = { scope.launch { scaffoldState.snackbarHostState .showSnackbar("Snackbar") } } ) } ) { // Screen content }
창
모달 탐색 창을 구현할 때 Scaffold 를 사용하는 방법과 사용하지 않는 방법 2가지가 존재한다.
- ColumnScope 인터페이스를 사용하여 열에서 콘텐츠를 배치하는 drawerContent 슬롯을 사용할 수 있다.
Scaffold( drawerContent = { Text("Drawer title", modifier = Modifier.padding(16.dp)) Divider() // Drawer items } ) { // Screen content }- Scaffold 없이 MoadlDrawer 컴포저블을 사용하면 된다.
val drawerState = rememberDrawerState(DrawerValue.Closed) ModalDrawer( drawerState = drawerState, drawerContent = { // Drawer content } ) { // Screen content }
- 하단 탐색 창을 구현하려면 BottomDrawer 컴포저블을 사용하면 된다.
val drawerState = rememberBottomDrawerState(BottomDrawerValue.Closed) BottomDrawer( drawerState = drawerState, drawerContent = { // Drawer content } ) { // Screen content }
하단 시트
표준 하단 시트를 구현하려면 BottomSheetScaffold 컴포저블을 사용하면 된다.
BottomSheetScaffold( sheetContent = { // Sheet content } ) { // Screen content }

모달 하단 시트를 구현하려면 ModalBottomSheetLayout 컴포저블을 사용하면 된다.
val sheetState = rememberModalBottomSheetState( ModalBottomSheetValue.Hidden ) ModalBottomSheetLayout( sheetState = sheetState, sheetContent = { // Sheet content } ) { // Screen content }
배경화면
배경화면을 구현하려면 BackdropScaffold 컴포저블을 사용하면 된다.
BackdropScaffold( appBar = { // Top app bar }, backLayerContent = { // Back layer content }, frontLayerContent = { // Front layer content } )

'Android' 카테고리의 다른 글
Android Retrofit2 사용을 위한 비동기 처리 (0) 2023.06.12 Android Studio 멀티 모듈과 Gradle Wrapper (0) 2023.05.15 Jetpack compose 레이아웃 - ① (0) 2023.05.09 Jetpack compose State 심화 (0) 2023.05.08 Jetpack compose Navigation (0) 2023.05.04