본문 바로가기
Android Jetpack Compose/Jetpack Compose

Jetpack Compose UI Part1 # Color Card Modifier Column Row

by Developer88 2022. 11. 11.
반응형

오늘은 안드로이드 Jetpack Compose에 대해서 알아보고,

기본적인 UI API들에 대해 정리하겠습니다.

 

1. Android Jetpack Compose

1-1. NoXML, welcome functions

이제 xml 필요없이 kotlin만으로,

Android의 UI까지 개발할 수 있게 되었는데요.

(그렇다고 xml을 사용할 수 없는 것은 아니구요.

하나의 도구가 더 생겼다고 보면 되겠습니다.)

 

만약, 텍스트, 라디오 버튼의로 구성된 UI가 가로로 있다고 가정해 보겠습니다.

이전에는 xml로 UI를 구성하고, 그것의 reference를 Kotlin코드에서 가져와서,

응답이나 반응등을 코드에서 구현하였는데요.

background 이미지를 바꾸거나, 색을 변경해 주었습니다.

 

이제는 아래와 같은 코드로 UI를 구성하게 되었습니다.

Compose에서는 UI 요소들은 더이상 객체가 아니라 함수입니다.

레퍼런스해서 가져오는 대상이 더이상 아니라는 것이지요.

 

 

 

UI요소들은 state이나 전달된 인자에 의해서 변경됩니다.

가져와서 setText하거나 할 필요도 없습니다.

 

1-2. Single Activity

Compose를 사용하면 Activity를 하나만 두고도 충분히 앱을 만들 수 있습니다.

물론, 여러 액티비티를 사용하는 것이 제한되는 것은 아니지만,

Fragment없이도 여러 페이지로 Navigation을 이용해서 이동하고,

데이터 전달을 할 수 있습니다.

이 부분에 대해서는 기본 UI에 대한 글을 모두 읽은 후에 아래 Navigation에 대한 글을 참조해 주시면 됩니다.

>> Navigation 과 Bottom Navigation 구현방법 정리 # Jetpack Compose

 

2. setUp

2-1. 빠른 setup

Jetpack Compose를 경험하기 위해서 여러가지 setup이 필요한데요.

기존 View와는 완전히 새로운 시스템이므로,

여러가지 설정이 필요합니다.

new Project에서 > Empty ComposeActivity 를 선택해 주고, Next를 눌러주면 

정말 빠르게 Jetpack Compose를 사용할 수 있도록 도와줍니다.

 

 

 

설정이 끝나고 나서,

안드로이드 스튜디오의 왼쪽네비게이션을 보면, res밑에 그동안 보이던 layout과 xml파일들이 없다는 것을 볼 수 있습니다.

 

 

3. setContent 와 Composable 함수

3-1. setContent

Activity에서 UI를 구현할 때,

이전처럼 setContentView()라고 하는 함수를 직접 사용하지 않고,

setContent 라는 함수를 사용하게 됩니다.

 

setContent함수안에서 Composable함수를 실행해서 UI를 보여주게 되는데요.

아래는 MessageBox()라는 함수가 실행되고 있습니다.

 

 

setContent함수의 코드를 보면, 주석에 나와있는 것처럼,

setContent 함수가 ComposeView와함께 setContentView함수를 호출해주고 있다는 것을 알 수 있습니다.

 

 

3-2. Composable 함수

아래에서는  Greeting이라는 Composable 함수가 있는데요.

"@Composable" Annotation 부분이 눈에 띕니다.

UI를 담당하는 Composable 함수라는 의미인데요.

함수로 따로 떼어내서 재사용 할 수 있는 UI를 얼마든지 만들 수 있습니다.

 

Kotlin의 문법을 그대로 따르므로 특별할 것은 없구요.

함수이지만, 대문자로 시작하는 네이밍컨벤션을 따른다는 점이 특이합니다.

 

 

위 코드에서 보이는 @Preview 는 안드로이드 스튜디오의 Preview화면에서 보기 위한 부분인데요.

Preview를 위한 코드를 일일히 작성해주어야 합니다.

preview를 위해서 Composable함수를 불러서 사용해 주어야 합니다.

xml보다 바로바로 직관적으로 preview하기가 어려워 졌다는 면이 아무래도 있습니다.

 

4.  기본 Layout Composable 

먼저 기본적인 Layout Composable 에 대해서 알아보겠습니다.

Column, Row, Box는 화면에 UI 요소들을 배치하는,

가장 기본적이고 핵심적인 레이아웃 컴포넌트입니다.

기존 Android XML의 LinearLayout이나 FrameLayout과 유사한 역할을 하니,

이것부터 먼저 이해해야 하겠지요.

 

4-1. Column 

Column은 내부의 자식 요소들을,

위에서 아래로 차례대로 배치합니다.

 

굳이 비교하자면,

예전의 LinearLayout (orientation="vertical")과 동일합니다.

 

먼저 @Composable Annotation을 붙인 Composable 함수를 만들어 줍니다.

세로로 요소들을 배열하는 Column안에 아래와 같이 2개의 Text를 배열해 보겠습니다.

 

 

 

결과는 다음과 같이 나옵니다.

 

 

4-2. Row

Row는 내부의 자식 요소들을,

왼쪽에서 오른쪽으로 배치할 때 사용합니다.

 

LinearLayout (orientation="horizontal")과 동일한데요.

위에서 본 Column과 같이 자식요소들을 감싸서 사용합니다.

 

 

4-3. Box

Box는,

내부에 있는 자식 요소들을, (Z축 방향으로) 쌓아서 배치합니다.

먼저 작성된 요소가 아래에 깔리고,

나중에 작성된 요소가 그 위에 덮어씌워집니다.

 

xml시대의 FrameLayout과 동일합니다.

예를 들어서, 이미지위에 텍스트를 올리려고 할 때,

Box가 필요하겠지요.

 

 

참고로 Box에서 alignment는,

Row나 Column과 다르게 Modifier를 사용해 주어야 합니다.

alignment는 아래에서 좀 더 알아볼께요.

 

5. arrangement 와 alignment

Arrangement와 Alignment는,

Row와 Column 안에서,

자식 요소들을 어느 위치에, 어떻게 간격을 두고 배치할 것인가를 결정하는,

매우 중요한 속성입니다.

 

이 2가지를 필요할 때 사용하려면,

'주축(Main Axis)'과 '교차축(Cross Axis)'의 개념을 이해해야합니다.

Row와 Column에서 주축과 교차축은 아래와 같이 달라집니다.

  • Row (가로 배치): 주축은 가로(수평)이고, 교차축은 세로(수직)
  • Column (세로 배치): 주축은 세로(수직)이고, 교차축은 가로(수평)

 

주축과 교차축을 이해했다면,

먼저 주축 방향으로 어떻게 나열할지를 결정하는,

Arrangement를 알아보겠습니다.

 

5-1. Arrangement

주축방향에서 나열하는 방법을 결정하는 arrangement는,

row와 column에서 아래와 같이 다르게 작동합니다.

 

  • Row (가로 간격/위치 조절):
    • Arrangement.Start: 왼쪽으로 모두 밀착
    • Arrangement.Center: 가운데로 모음
    • Arrangement.End: 오른쪽으로 모두 밀착
    • Arrangement.SpaceBetween: 양끝을 벽에 붙이고, 가운데 여백을 동일하게 나눔
    • Arrangement.SpaceAround: 각 요소의 양옆 여백을 동일하게 줌
    • Arrangement.SpaceEvenly: 요소 사이와 양끝 벽까지의 모든 여백을 완전히 동일하게 줌
  • Column에서의 Arrangement (세로 간격/위치 조절):
    • Top, Center, Bottom 기준으로 작동

 

5-2. Alignment (정렬)

Alignment는 교차축(Cross Axis) 방향으로,

요소들을 어디에 정렬할지 결정합니다.

요소들 간의 간격과는 무관하며,

위, 중간, 아래 (또는 왼쪽, 중간, 오른쪽) 중 어디에 붙일 것인지를 결정합니다.

 

  • Row에서의 Alignment (세로 위치 정렬):
    • Alignment.Top: 위쪽 벽에 맞춤
    • Alignment.CenterVertically: 세로 중앙에 맞춤
    • Alignment.Bottom: 아래쪽 벽에 맞춤
  • Column에서의 Alignment (가로 위치 정렬):
    • Alignment.Start: 왼쪽 벽에 맞춤
    • Alignment.CenterHorizontally: 가로 중앙에 맞춤
    • Alignment.End: 오른쪽 벽에 맞춤

 

실제 구현된 코드를 보고 이해해 볼까요?

Row안의  Image와 Column을 넣고,

Column안에 텍스트 2개를 배열했는데요.

각각 교차축의 정렬에 대해서, Alignment를 이용해 정의해 사용했습니다.

 

 

 

참고로 Text에는 아래와 같이 다양한 TextStyle을 적용할 수 있으므로,

여러가지를 실험해보면서 적용해보면 좋은데요.

Text사이즈를 사용할 때,

아래와 같이 ".sp"형태로 붙여서 사용합니다.

 

 

5-3. contentAlignment

Box는 내부에 하나의 공간만 있기 때문에,

주축/교차축 개념 대신,

contentAlignment라는 하나의 속성으로, 

상하좌우 정렬(예: Alignment.TopEnd, Alignment.Center)을 동시에 처리하는데요.

 

상/중/하와 좌/중/우를 조합하여,

총 9가지의 위치(예: TopStart, Center, BottomEnd 등)를 지정할 수 있습니다.

 

---

 

몇가지 예제를 보면서 이해해 볼께요.

아래는 Box 안의 모든 요소를,

contentAlignment = Alignment.Center 를 주어서,

'정중앙'에 겹쳐서 배치하는 경우입니다.

 

예제의 modifier는 아래에서 정리할테니,

우선은 이런 것이 있다고만 봐주세요.

 

@Composable
fun LoadingScreenBox() {
    Box(
        modifier = Modifier
            .fillMaxSize()
            .background(Color.LightGray),
        contentAlignment = Alignment.Center 
    ) {
        CircularProgressIndicator()       
        Text(
            text = "로딩 중...",
            modifier = Modifier.padding(top = 88.dp)
        )
    }
}

 

아래에서는,

가장 바깥의 Box에서는, 

정렬을 '정중앙'으로 설정하였고요.

 

그 안의 Box는 정의를 하지 않았는데,

바깥에서 정의한 기본정렬인 Center가 됩니다.

(contentAlignment를 아예 작성하지 않으면, 

Box의 기본(Default) 정렬 위치는 Alignment.TopStart (좌측 상단)가 됩니다)

 

내부 요소들을 한가운데에 모아두고 그중 일부만 위치를 조정하고 싶다면,

아래처럼 부모 Box에 contentAlignment = Alignment.Center를 꼭 적어주고,

필요한 요소에 변경을 주면 됩니다.

 

@Composable
fun ProfileWithBadge() {
    Box(
        modifier = Modifier
            .size(100.dp)
            .background(Color.White),
        contentAlignment = Alignment.Center 
    ) {
        // 기본 정렬(Center)을 따라 정중앙에 배치
        Box(
            modifier = Modifier
                .size(80.dp)
                .clip(CircleShape)
                .background(Color.Blue)
        )

        // 우측 하단(BottomEnd)'으로 이동
        Box(
            modifier = Modifier
                .size(24.dp)
                .clip(CircleShape)
                .background(Color.Red)
                .align(Alignment.BottomEnd) // 부모의 contentAlignment 덮어쓰기!
        ) {
            Text(
                text = "N", 
                color = Color.White, 
                modifier = Modifier.align(Alignment.Center) 
            )
        }
    }
}

 

6.  Modifier - weight, width, height, background, border

위에서 해보았던 간단한 alignment나 arrangement 이외에도,

다양한 Modifier를 이용해 더욱 많은 레이아웃 옵션을 설정할 수 있습니다.

 

6-1. weight

Column()과 Row()에는 weight를 사용해서 비율을 정해줄 수 있습니다.

아래와 같이 1:1로 해 준다면, 전체 사이즈의 절반씩 가져가게 될 것이구요.

 

 

 

아래와 같이 3:7로 해서 실행해 보겠습니다.

 

 

아래와 같이 3:7 비율로 실행되는 것을 볼 수 있습니다.

 

 

6-2. Width, Height, fillMax

이번에는 Layout의 Row의 가로세로 사이즈에 대해서 설정해 보겠습니다.

아래에서는 fillMaxWidth와 height 그리고 background를 사용하였는데요.

각각 다음과 같은 역할을 해 줍니다.

 

  • fillMaxWidth, fillMaxHeight: 가로 또는 세로의 최대길이를 차지
  • width, heigth: 가로와 세로를 dp단위로 지정
  • fillMaxSize: 가로와 세로 부모의 사이즈를 최대로 차지

 

fillMaxWidth를 하면 화면의 100%를 채우겠지만,

인자로 아래와 같이 float값을 주면 그 비율만큼만 차지하게 할 수 있습니다.

 

참고로,

Box를 사용할 때 Modifier.fillMaxSize()를 사용하는 경우를 많이 보는데요.

이는 컨테이너의 크기가 결정되어야,

내부 정렬(Alignment)이 의미를 갖기 때문입니다.

 

아래에서 height는 dp로 설정하였는데요.

dp단위를 나타낼 때는 아래와 같이 닷(".")을 이용해 주면 됩니다.

background는 아래와 같이 쉽게 표현할 수 있습니다.

 

 

 

앱을 실행해보면 아래와 같이 렌더링 된 것을 볼 수 있습니다.

 

 

border도 적용이 가능한데요. 

modifier들은 코드를 작성한 순차적으로 적용이 되구요.

중첩은 되더라도 나중에 쓰여진 것이 위로 쌓이게 됩니다.

이를 이용하면 중첩된 border의 작성도 가능하겠지요.

 

 

실행해보면 다음과 같이 중첩되어진 것을 볼 수 있습니다.

 

 

6-3. requireWidth, requireHeight

한가지 더 보고 갈 것이 있는데요.

requireWidth()나 requireHeight() 입니다.

이것은 최소한의 가로 또는 세로인데요.

미니멈값으로 차지하고 있어야 하는 길이를 말합니다.

 

6-4. padding

이번에는 modifier로 패딩을 적용해 보겠습니다.

8dp를 Image 주변에 적용하였는데요.

 

 

실행해보면 다음과 같이 패딩이 적용된 것을 볼 수 있습니다.

 

 

아래와 같은 방식으로, 각 위치별로도 적용할 수 있습니다.

 

 

6-5. offset

Compose에서는 margin이 사라지고, offset이라는 개념만 남았는데요.

margin 을 대체하긴 하였지만 동일한 개념은 아닙니다.

 

offset은 자신을 특정 요소로부터 멀어지게는 하는데요.

margin처럼 다른 요소를 밀어내지는 못합니다.

즉, 잘못하면 다른 요소와 겹쳐져 버릴 수 있다는 뜻 입니다.

 

아래와 같이 하나의 요소에 offset을 주었습니다.

 

 

 

실행해보면 뒤로 멀어진 것을 볼 수 있습니다.

 

 

참고로 이 요소는 top이나 bottome이 아니라,

x나 y로 가로세로로 범위를 지정해 줍니다.

 

 

offset의 코드를 보면 다음과 같습니다.

마이너스 값도 지정할 수 있다고 나와있네요.

 

 

6-6. Clickable

Click이벤트가 필요하면 쉽게 이를 활용할 수 있습니다.

clickable은 lambda를 이용해서 아래와 같이 쉽게 작성할 수 있습니다.

참고로 Composable에서는 context를 아래와 같이 "LocalContext.current"에서 얻어와야 합니다.

 

 

 

이외에도 draggable, Scrollable 도 쉽게 적용시킬 수 있습니다.

Scrollable은 State가 필요한데요.

State에 대해 다루는 다음 글 이후에 다루도록 하겠습니다.

 

4-6. Card

머티리얼 디자인 요소가 적용된 카드 layout입니다.

shape이나 elevation, border, shadow등을 설정할 수 있습니다.

 

 

아래 4에서 실제로 CardUI를 만들어 보면서 좀 더 정리해 보도록 하겠습니다.

 

6-7. Spacer

빈 공간을 가지고 있는 비어져 있는 요소입니다.

위에서 offset은 밀려나면서 다른 요소를 침범해 겹쳐져 버릴 수 있다고 하였는데요.

이런 Space를 중간에 두면 그런 것에대한 방어가 될 수 있겠지요.

 

7. Card 와 Box 를 이용한 Card UI

위에서 보았던 Card와 Box를 이용해서 Card UI를 만들어 보도록 하겠습니다.

가장 먼저 할 것은 Composable을 작성하는 것 입니다.

 

 

이제 이 함수에 인자로 받아들일 것들을 생각해 봐야 합니다.

ImageCard에 들어갈 string이 필요할 것이구요.

이미지도 불러와야 합니다.

그런데, 이미지를 설정할 때 마다 다르게 해 주어야 하므로,

인자로 받아서 설정해 주어야 합니다.

그렇게 하기위해서, Composable의 Painter타입의 painter도 추가해 줍니다.

 

 

구조는 다음과 같이 넣도록 하겠습니다.

Card안에 Column이 들어가고,

세로로 Image와 Text 그리고 Text를 하나 더 배열하는 것 이지요.

 

Card > Column > Image, Text, Text

 

먼저 Card 안에 Column을 아래와 같이 배치해 줍니다.

특별한 것은 없구요. Card에 border와 shape을 정해주는데요.

shape을 설정해 줄 때는 RoundedCornerShape형태로 해 줍니다.

 

 

 

이제 Column안에 Image와 Text2개를 연속으로 배열합니다.

 

 

이 카드는 50% 비율로 화면에 차지하게 하기 위해서, 아래와 같이 setContent에서 배열해 줍니다.

 

 

이것을 실행해 보면 아래와 같은 화면이 나오는 것을 볼 수 있습니다.

 

 

이제 저 이미지 위에 텍스트를 오버레이해서 올려보겠습니다.

이미지가 있던 Box안에, 다시 Box를 쌓아올리고, 그 안에 text를 올렸습니다.

 

 

실행해보면 다음과 같이 나오는 것을 볼 수 있습니다.

 

 

8. Custom 컬러 설정

8-1. Hex값의 입력

컬러의 경우 아래와 같이 #대신 0xFF로 대신해서 넣어주면 됩니다.

 

Color클래스의 companion object를 보면 다음과 같은데요.

Black은 #000000 인데 이것을 0xFF000000 으로 바꿔준 것을 볼 수 있습니다.

 

 

아래와 같이 넣어주면 됩니다.

 

 

8-2. toColorInt

안드로이드의 Kotlin라이브러리 중 하나인 Core-KTX에서는,

String의 확장함수인 toColorInt()라는 API를 제공해주는데요.

String hex값을 Color클래스가 필요로하는 Int값으로 변환시켜 줍니다.

 

 

이를 활용해서 쉽게 String hex값을 적용해 줄 수 있습니다.

 

 

 

8-3. Button 컬러의 변경

버튼 컬러를 변경할 때는 조금 다른 방법을 사용해 주어야 하는데요.

기존의 Button에 보면 아래와 같이 컬러가 정의된 것을 볼 수 있습니다.

 

 

colors에 정의하는 컬러 타입이 ButtonColors여야 하는데요.

아래 코드 주석에 나오는 것과 같이,

backgroundColor는 버튼의 배경색이구요.

contentColor는 버튼에 올라오는 컨텐츠의 색깔입니다.

disable되었을 때의 색깔은 disabledBackgroundColor와 disabledContentColor를 설정해주면 됩니다.

 

 

아래와 같이 buttonColors 함수를 이용해 주기만 하면 됩니다.

컨텐츠가 있을 경우에는 contentColor를 넣어주면 됩니다.

버튼에 텍스트가 있을 경우 특히 유효하겠지요.

 

 

8-4. Icon Color 변경

Icon클래스를 보면 다음과 같이 정의되어 있는데요.

 

 

Color타입이기는 하지만 아래와 같이 copy값에 아래와 같이 alpha값을 설정해 주면 됩니다.

Material 벡터 Icon의 컬러를 변경해 주려면 다음과 같이 해주면 됩니다.

 

 

 

8-5. copy함수의 활용

한가지 Color 클래스에는 copy함수가 있는데요.

원래 색깔에서 alpha나 red, green, blue 값을 변경할 수 있습니다.

색보정앱이 아니고서야, alpha정도 변경하는데 도움이 되겠지요.

 

 

아래와 같이 적용해 볼 수 있습니다.

 

 

여기까지 part1에서 Row, Column, Modifier, Card, Composable Functions, Color 에 대해서 정리해 보았습니다.

728x90

댓글