본문 바로가기

안드로이드 Android

안드로이드 개발 (13) Layout in Compose 1편

 

지금까지 Compose에 대해 composable 라이프사이클, Compose 내부흐름 , Composition, recompostion, Sdie-effects 활용방법에 대한 원론 방법에 알았다면 이제 실질적으로 Compose로 Layout을 어떻게 구성하는지에 대해 공부를 해봤습니다.

0. 개요

- Composable 함수를 통해서 여러 UI요소들을 만들 수 있음

- 하지만 UI요소들에 대한 정렬 기준이 없다면 아래와 같이 생성됨 

예제.

@Composable
fun ArtistCard() {
    Text("Alfred Sisley")
    Text("3 minutes ago")
}

결과.

레이아웃이 없으면 위와같이 UI요소를 여러개 사용할 때 나오기 때문에 필수로 있어야함

1. 기본 레이아웃 

기본적으로 사용되는 레이아웃은 3가지가 있습니다. Column , Row, Box

 

1) Colum, Row

Colum 은 수직방향으로 UI를 정렬 (리니어레이아웃의 수직 모드와 흡사함)

Row 는 수평방향으로 UI를 정렬

 

예제.

    @Composable
    private fun UserData() {
        Row(verticalAlignment = Alignment.CenterVertically) {
            Image(
                painter = painterResource(id = R.drawable.header),
                contentDescription = null,
                contentScale = ContentScale.Crop,
                modifier = Modifier
                    .padding(8.dp,12.dp,8.dp,12.dp)
                    .size(44.dp)
                    .clip(shape = CircleShape)
            )
            Column {
                Text("name")
                Text(
                    "lastSeenOnline",
                    Modifier.padding(0.dp, 0.dp, 8.dp, 0.dp)
                )
            }
        }
    }

결과. 

Row와 Colum을 조합해서 위 같은 UI를 요소를 만들 수 있습니다.


2) Box

Box는 UI요소를 다른 요소위에 배치가 가능함 (FrameLayout과 흡사하다.)

예제.

    @Composable
    fun AlignInRow() {
        Row(
            modifier = Modifier
                .size(150.dp)
                .background(Color.Yellow),
            horizontalArrangement = Arrangement.End,
            verticalAlignment = Alignment.CenterVertically
        ) {
            Box(
                Modifier
                    .size(50.dp)
                    .background(Color.Red)
            )

            Box(
                Modifier
                    .size(50.dp)
                    .background(Color.Green),
                contentAlignment = Alignment.Center
            ) {
                Box(
                    Modifier
                        .size(30.dp)
                        .background(Color.Blue)
                )
            }

        }
    }

- 겹쳐서 사용이 가능하다.

 

결과.

- Compose는 중첩된 레이아웃을 효율적으로 처리

- 기존 XML으로 중첩을 그리는것을 권장하지 않는것과 달리 Compose는 중첩된 레이아웃을 사용해도 성능상 크게 문제가 없음

 

기본 레이아웃을 봤을때 리니어 레이아웃의 수직, 수평과 프레임 레이아웃으로 만드는듯한 느낌이 날거같습니다.

기존 오버드로우를 피하기 위해서 XML에서 컨스트레이아웃 사용을 권장했던 것과 달리 코드로 가로 세로 중첩 이 3가지 모드로 UI 정렬 규칙을 정하고 빠른 시간안에 UI요소들을 만들겁니다.

 

 

 

 

2. Modifiers (수정자)

1) 수정자 특징 

위 예제들에서 이미 사용했던 Modifier 에 관한 설명입니다.

 

-Modifiers는 Kotlin 객체 

-Modifier.함수()로 사용 

-Modifiers를 통해 다음과 같은 작업들을 할 수 있음

  • Composable의 크기, 레이아웃, 동작 및 모양 변경
  • 접근성 라벨과 같은 정보 추가
  • 사용자 입력 처리
  • UI요소를 클릭 가능, 스크롤 가능, 드래그 가능 또는 확대/축소 가능하게 만드는 것과 같은 높은 수준의 상호작용 추가

예제.

  @Composable
    fun ArtistCard(
        onClick: () -> Unit
    ) {
        val padding = 16.dp
        Column(
            Modifier
                .clickable(onClick = onClick)
                .padding(padding)
                .fillMaxWidth()
        ) {
        //userData()는 위에 만들어둔 예제
            UserData()
            Spacer(Modifier.size(padding))

            Card(elevation = 4.dp) {
                Image(
                    painter = painterResource(id = R.drawable.header),
                    contentDescription = null,
                    modifier = Modifier.fillMaxWidth(),
                    contentScale = ContentScale.Crop

                )
            }
        }
    }

위 예제를 보면 Colum, Spacer, Image 인자로  Modifier를 사용해서 추가적인 속성들을 정의를 한 것을 볼 수 있습니다.

 

결과

 

 

 *Modifiers의 장점

 

- Modifiers는 때로는 범위에 따라 다르므로 유형 안전성을 제공

- 특정 레이아웃에 사용 및 적용이 가능한 요소를 찾고 이해를 도와줌 

- XML 레이아웃을 사용하면 특정 레이아웃 속성이 지정된 뷰에 적용 가능한지를 확인하기 어려운데, 

Modifiers는 판별이 가능함 

 

** Modifiers는 순서가 매우 중요함 **

 

예제1. 

 @Composable
    fun ArtistCard(
        onClick: () -> Unit
    ) {
        val padding = 16.dp
        Column(
            Modifier
                .clickable(onClick = onClick)
                .padding(padding)
                .fillMaxWidth()
        ) {
      .....
        }
    }

-Colum인자로 Modifier의 함수 체어이닝 순서가 (1) clickable() (2) padding() 순서일 때 clickable이 먼저 적용됨 그래서 아래와 같은 결과가 나옴

 

 

click시 repple영역이 padding 적용전의 범위로 지정됨 

 

예제2.

 @Composable
    fun ArtistCard(
        onClick: () -> Unit
    ) {
        val padding = 16.dp
        Column(
            Modifier
                .padding(padding)
                .clickable(onClick = onClick)
                .fillMaxWidth()
        ) {
      .....
        }
    }

-이전과 달리 순서를 바꾸면  (1) padding() (2) clickable() 순서일 때 padding이 먼저 적용되고 clickable이 적용됨 

 

뷰를 click 시 padding이 적용된 영역까지만 repple이 생김 

 

 

2) Modifiers 명시적인 순서에 대한 안드로이드 문서 설명

Note: The explicit order helps you to reason about how different modifiers will interact. 

Compare this to the view-based system where you had to learn the box model, that margins applied "outside" the element but padding "inside" it, and a background element would be sized accordingly. 

The modifier design makes this kind of behavior explicit and predictable, and gives you more control to achieve the exact behavior you want. 

It also explains why there is not a margin modifier but only a padding one.

 

"명시적인 순서는 다양한 수정자가 상호작용하는 방식을 추론하는 데 도움이 됩니다.

박스 모델을 학습해야 했던 뷰 기반 시스템과 이를 비교해 보세요.

박스 모델의 경우 여백은 요소 '외부'에 적용되었지만 패딩은 '내부'에 적용되었으며 백그라운드 요소는 그에 따라 크기가 조정되었습니다.

수정자 디자인을 사용하면 이러한 종류의 동작이 명확해지고 예측 가능하게 되며 원하는 동작을 정확하게 달성할 수 있도록 추가적으로 제어할 수 있습니다.

padding 수정자를 제외한 여백 수정자가 없는 이유도 설명할 수 있습니다."

 

3) 이외의 modifiers 정보

(1) 지정한 크기가 레이아웃의 상위 요소를 무시하고

   Composable의 크기를 고정해야 하는 경우 requiredSize 수정자를 사용

 

예제.

@Composable
fun FixedSizeComposable() {
    Box(Modifier.size(90.dp, 150.dp).background(Color.Green)) {
        Box(Modifier.requiredSize(100.dp, 100.dp).background(Color.Red))
    }
}

- 가장 하위에 있는  Box가 상위 Box 90.dp 크기를 무시하고 100.dp로 설정됩니다.

 

 

(2) 레이아웃의 상위 요소 크기에 상위요소가 허용하는 만큼 하위요소가 꽉 채우고 싶은 경우

fillMaxSize(), fillMaxWidth(), fillMaxHeight() 등등을 골라서 사용

 

예제.

@Composable
fun FillSizeComposable() {
    Box(Modifier.background(Color.Green).size(50.dp).padding(10.dp)) {
        Box(Modifier.background(Color.Blue).fillMaxSize())
    }
}

-fillMaxSize() 사용으로 상위 UI요소 만큼 하위 UI요소의 크기가 지정됨

-padding을 넣엇기 때문에 패딩이 10dp가 적용됨 

 

 

(3) 하위 레이아웃을 상위 요소와 동일한 크기로 설정하려면 matchParentSize 수정자를 사용합니다.

@Preview
    @Composable
    fun MatchParentSizeComposable() {
        Box {
            Spacer(
                Modifier
                    .matchParentSize()
                    .background(Color.Green))
            Text("Hello World")
        }
    }

 

(4) 레이아웃 상단에서 기준선까지 특정 거리가 유지되도록 패딩을 추가하려면 paddingFromBaseline 수정자를 사용

@Composable
fun TextWithPaddingFromBaseline() {
    Box(Modifier.background(Color.Yellow)) {
        Text("Hi there!", Modifier.paddingFromBaseline(top = 32.dp))
    }
}

 

4) Offset

- 원래 위치를 기준으로 레이아웃을 배치해줌

- offset 수정자를 추가해서 x축 , y축을 설정해서 사용함

- 양수 혹은 음수일 수 있음

- offset의 측정값은 변경되지 않음 

- offset 수정자는 레이아웃 방향에 따라 가로로 적용

- LTR 컨텍스트에서 양수 offset은 요소를 오른쪽으로 이동시킴

- RTL 컨텍스트에서는 요소를 오른쪽으로 이동합니다.

- aboluteOffset은 레이아웃 방향 상관없이 항상 오른쪽으로 이동시킴 

 

예제.

@Composable
fun OffsetComposable() {
    Box(Modifier.background(Color.Yellow).size(width = 150.dp, height = 70.dp)) {
        Text(
            "Layout offset modifier sample",
            Modifier.offset(x = 15.dp, y = 20.dp)
        )
    }
}

 

결과.

 

3. 스크롤 가능한 레이아웃

https://developer.android.com/jetpack/compose/gestures?hl=en#scroll-modifiers 

 

동작  |  Jetpack Compose  |  Android Developers

Compose는 사용자 상호작용에서 생성된 동작을 감지하는 데 도움이 되는 다양한 API를 제공합니다. 이 API는 광범위하게 사용됩니다. 그중 일부는 상위 수준이며 가장 일반적으로 사용되는 동작을

developer.android.com

참고 (블로그 포스팅 예정)

 

4. 반응형 레이아웃

Compose에서 제공하는 즉시 사용 가능한 몇 가지 메커니즘으로

composable 레이아웃을 다양한 화면 구성에 따라 쉽게 조정할 수 있음

1) Row  Column의 가중치 수정자

- Composable 크기를 상위 요소 내에서 유연하게 설정할 수 있습니다.

예제.

@Composable
fun FlexibleComposable() {
    Row(Modifier.width(210.dp)) {
        Box(Modifier.weight(2f).height(50.dp).background(Color.Blue))
        Box(Modifier.weight(1f).height(50.dp).background(Color.Red))
    }
}

- 첫 번째 상자의 weight가 두 번째 상자의 두 배로 지정되므로 너비가 두 배로 지정

- 첫 번째 Box의 너비는 140.dp,  두 번째는70.dp

 

2) 제약 조건

- 상위 요소의 제약 조건에 따라 레이아웃을 디자인하려면 BoxWithConstraints를 사용 

- 측정 제약조건은 콘텐츠 람다의 범위에서 확인 가능 

- 측정 제약 조건을 사용하여 다양한 화면 구성에 따라 다양한 레이아웃을 구성이 가능 

@Composable
    fun WithConstraintsComposable() {
        BoxWithConstraints {
            Text(
                "My minHeight is $maxHeight while my maxWidth is $maxWidth",
                fontSize = 24.sp
            )
        }
    }

- 최대 dp를 알수도 있고, 제약 관련 정보들이 있다.

 

5. 슬롯 기반 레이아웃

 

UI를 쉽게 빌드할 수 있도록 머티리얼 디자인  androidx.compose.material:material 종속 항목을 기반으로

한 다양한 Composable을 제공합니다. 

 

- Drawer, FloatingActionButton  TopAppBar와 같은 요소가 모두 제공

-머티리얼 구성요소는 Composable 위에 맞춤설정 레이어를 배치하기 위해 도입한 패턴인 슬롯 API를 많이 사용

 (하위 요소의 모든 매개변수를 노출하지 않고 하위 요소를 구성할 수 있어서 유연성이 향상됨)

-슬롯은 개발자가 원하는 대로 채울 수 있도록 UI에 빈 공간을 남겨둠 

 

    @Composable
    fun HomeScreen(/*...*/) {
        Scaffold(
            drawerContent = { Text("drawer example") },
            topBar = {
                TopAppBar(
                    title = { Text("Scaffold 예제", textAlign = TextAlign.Center) },
                    backgroundColor = Color.Gray
                )
            },
            content = { Text(text = "슬롯 API 테스트") }
        )
    }

 

- 아주 쉽게 AppBar, drawer 를 만들수 있음 

- 기존 XML 에서 만들던 방식에 비해 상당히 적은 노력이 들어간다는것이 아주 매력적임 

6. 정리

- Colum, Row, Box가 기본 레이아웃으로 사용됨 

- 중첩 레이아웃으로 인한 성능 저하를 걱정하지 않아도 됨

- Modifiers로 Composable의 속성들을 바꿀 수 있음 각종 확장 기능들로 다양하게 활용가능 

- Modifiers는 순서가 매우 중요 명시적인 순서를 뛰며 순서에 따라 결과가 다름

- Offset으로 기본 위치로 부터 절대 측정값을 사용할 수 있음

- 반응형 레이아웃으로 유연한 UI그리기 가능 (가중치 적용, 제약조건 이용)

- 슬롯 기반 레이아웃 지원 API

 

 

이전 Compose 정리

https://gift123.tistory.com/33?category=967702 

 

안드로이드 개발 (8) Compose 이해 정리

이번 포스팅부터 Compose에 대해 차근히 파헤쳐 가보겠습니다. Android Compose 공식 문서를 보면서 정리한 내용들 입니다. https://developer.android.com/jetpack/compose/mental-model?hl=en Compose 이해  |..

gift123.tistory.com

https://gift123.tistory.com/34?category=967702 

 

안드로이드 개발 (9) Compose 상태 관리

jetpack compose 에 한창 포스팅 중입니다. https://gift123.tistory.com/33 안드로이드 개발 (8) Compose 이해 정리 이번 포스팅부터 Compose에 대해 차근히 파헤쳐 가보겠습니다. Android Compose 공식 문서를..

gift123.tistory.com

 

https://gift123.tistory.com/38?category=967702 

 

안드로이드 개발 (10) Compose Composable Lifecycle

안녕하세요 Loner 입니다. 오늘은 Compose의 컴포저블 라이프사이클 공부를 정리해 봤습니다. 아래 문서를 정리한 내용입니다. https://developer.android.com/jetpack/compose/lifecycle?hl=en 컴포저블 수명 주..

gift123.tistory.com

https://gift123.tistory.com/39?category=967702 

 

안드로이드 개발 (11) Compose Side-effects

https://developer.android.com/jetpack/compose/side-effects#state-effect-use-cases Compose의 부수 효과  | Jetpack Compose  | Android Developers 컴포저블에는 부수 효과가 없어야 합니다. 하지만 앱의..

gift123.tistory.com