깨알 개념/Android

[Android] Jetpack Compose - Modifier

interfacer_han 2024. 7. 22. 02:22

#1 Modifier

#1-1 개요

 

Modifier  |  Android Developers

androidx.compose.desktop.ui.tooling.preview

developer.android.com

Jetpack Compose를 사용하지 않는 전통적인 방식의 안드로이드 프로젝트에서 우리는 XML 속성과 값을 사용하여 텍스트의 너비, 높이, 표시할 텍스트, 크기, 스타일 및 색상, 배경 색상 및 패딩을 지정했다. 이와 같은 UI 꾸밈 요소를 Jetpack Compose에서는 Modifier라는 클래스가 담당한다. Composable 함수들은 매개변수로 modifier: Modifier = Modifier를 가지고 있는데, 이 Modifier를 함수 외부에서 인수로서 전달하는 방식으로 UI를 꾸민다.

 

또, 기존 XML에서 위젯의 속성을 설정하는 행위가 꼭 UI 꾸밈이라는 시각적 설정에 한정되는 것은 아니었다. 사용자 입력 처리나 스크롤 가능 등의 동작도 포괄했었다. Modifier도 마찬가지로 UI 위젯의 어떤 동작까지도 설정해줄 수 있다.

 

#1-2 메소드 체이닝을 통한 구현

val myModifier1 = Modifier
    .background(color = Color.Gray) // 배경색을 회색으로
    .border(10.dp, color = Color.Magenta) //  10dp 두께의 Magenta 색을 가진 border 설정
    .padding(10.dp) // 10dp만큼 padding 설정

Modifier는 위와 같이 메소드 체이닝을 통해 구현된다. 이 말은, 메소드 체이닝의 순서가 매우 중요해진다는 의미가 된다.

 

#1-3 메소드 체이닝 '순서'의 중요함

val myModifier2 = Modifier
    .wrapContentSize()
    .background(color = Color(0xFFFFC896))
    .border(10.dp, color = Color.White)
    .padding(10.dp)

val myModifier3 = Modifier
    .background(color = Color(0xFFFFC896))
    .border(10.dp, color = Color.White)
    .padding(10.dp)
    .wrapContentSize() // 안 먹힘. 이미 fillMaxSize()을 사용하는 background(), border(), padding()이 메소드 체이닝된 후이기 때문.

UI의 주요한 꾸밈 요소 중 하나는 해당 UI 위젯의 크기 설정일 것이다. Modifier의 크기 기본값은 fillMaxSize()로 설정되어있다. 즉, wrapContentSize() 등으로 크기를 명시해주지 않으면 기본값인 fillMaxSize()가 적용된다. 이 때, 위의 코드를 보자. myModifier2와 myModifier3는 같은 Modifier일까? 아니다. 순서가 다르기 때문이다.

 

myModifier2는 메소드 체이닝의 첫번째 순서로 wrapContentSize()을 사용했다. 이 선제적인 사용에 의해서 뒤에 오는 메소드인 background(), border(), padding()이 fillMaxSize() 기준으로 그려지는 것을 막았다. 반면, myModifier는 크기 조절 메소드인 wrapContentSize()를 맨 마지막에 사용했는데, 이미 background(), border(), padding()가 기본 크기인 fillMaxSize()를 기준으로 이미 그려진 이후기 때문에, wrapContentSize()는 먹히지 않는다. 즉, myModifier3는 여전히 fillMaxSize()라는 것이다. 결론적으로, myModifier3의 메소드 체이닝은 실패했다.

 

#2 Modifier를 사용한 코드

// package com.example.modifier

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.modifier.ui.theme.ModifierTheme

val myModifier1 = Modifier
    .background(color = Color.Gray)
    .border(10.dp, color = Color.Magenta)
    .padding(10.dp)

val myModifier2 = Modifier
    .wrapContentSize()
    .background(color = Color(0xFFFFC896))
    .border(10.dp, color = Color.White)
    .padding(10.dp)

val myModifier3 = Modifier
    .background(color = Color(0xFFFFC896))
    .border(10.dp, color = Color.White)
    .padding(10.dp)
    .wrapContentSize() // 안 먹힘. 이미 fillMaxSize()을 사용하는 background(), border(), padding()이 메소드 체이닝된 후이기 때문.

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ModifierTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = Color.Black
                ) {
                    Greeting("Android", myModifier1)
                }

                Surface(
                    modifier = Modifier.fillMaxSize(0.5f),
                    color = Color.Red
                ) {
                    Greeting(name = "Jetpack Compose", myModifier2)
                }
            }
        }
    }
}

@Composable
fun Greeting(name: String, modifierParam: Modifier = Modifier) {
    Text(
        text = "Hello $name!",
        fontSize = 32.sp,
        color = Color.Blue,
        fontWeight = FontWeight.Bold,
        textAlign = TextAlign.Center,
        modifier = modifierParam
    )
}

#1-2 ~ #1-3에 있는 Modifier를 이용해 코드를 짰다. 2개의 Surface를 사용했는데, Layout적으로 아무것도 명시하지 않았기 때문에 둘은 그냥 겹쳐져 있을 것이다.

 

#3 작동 확인

#3-1 myModifier1과 myModifier2 사용

"Hello Android!"를 표시하는 Greeting()에는 myModifier1가 적용됐다. myModifier1에는 크기에 관해 아무것도 명시하지 않았기에 기본값인 fillMaxSize()가 적용되었고 따라서 Surface를 가득 채웠다. 그래서 Surface의 color 속성을 검은색으로 설정했음에도, 화면에 검은색이 보이지 않는다. myModifier1의 배경색인 회색이 Surface를 가득 채워버렸기 때문이다.

 

"Hello Jetpack Compose!"를 표시하는 Greeting()에는 myModifier2가 적용됐다. 이 Greeting()이 담긴 Surface 자체 Modifier에 크기를 1/4로 명시해서 크기가 작다. myModifier2는 wrapContentSize() 메소드가 적용되어 Greeting()이 Surface를 가득 채우지 않는다. 그래서 Surface의 배경색인 빨간색을 확인할 수 있다. 

 

#3-2 myModifier1과 myModifier3 사용

다른 조건은 유지한 채로 myModifier2 자리에, 이른바 실패한 메소드 체이닝을 했던 myModifier3를 넣었다. myModifier3를 만들 때 수행했던, 메소드 체이닝의 마지막 체인인 wrapContentSize()가 적용되지 않은 모습이다. Greeting()이 Surface를 가득 채워 빨간색 배경을 확인할 수 없다.

 

#4 요약

Modifier는 UI Setter다.

 

#5 완성된 앱

 

android-practice/jetpack-compose/Modifier at master · Kanmanemone/android-practice

Contribute to Kanmanemone/android-practice development by creating an account on GitHub.

github.com