Jetpack Compose Essentials) 1. Write your first Compose app (2)

ERyukSa·2023년 7월 4일
0

Developers 홈페이지에 있는 컴포즈 트레이닝 코스 Jetpack Compose Essentials의 4번째 챕터인 코드랩 Write your first Compose app의 5~6번을 정리한 글입니다.

5. 재사용 가능한 컴포즈 함수

UI에 컴포넌트를 추가하면 할수록 많은 중첩 구조를 만들게 된다. 그런 구조는 커질수록 가독성에 영향을 준다.

그러므로 작고 재사용 가능하도록 UI 컴포넌트(컴포즈 함수)를 만들자. 재사용 가능한 컴포넌트가 쌓일수록 UI를 구축하는 작업은 수월해진다.

각 함수는 적어도 화면의 작은 부분을 담당해야 하며 독립적으로 변경될 수 있어야 한다.

특정 Modifier에 의존하지 않도록 만든다

재사용 가능한 컴포즈 함수의 Best practice 구현은 함수에 Modifier 파라미터를 포함시키는 것이다. 그리고 empty Modifer를 기본 값으로 설정한다.

이 modifier는 자신이 호출하는 또다른 컴포즈에 전달할 수도 있다.

무슨 뜻인지 감이 안올 수 있다. 우선 예시를 보자.

@Composable
private fun MyApp(modifier: Modifier = Modifier) {
    Surface(
        modifier = modifier,
        color = MaterialTheme.colorScheme.background
    ) {
        Greeting("Android")
    }
}

onCreate에 있던 코드를 별도의 함수 MyApp으로 분리했다.
Empty Modifer를 기본 파라미터로 갖고 있고, 이것을 자신이 호출하는 또다른 컴포즈, Surface에 전달한다.

이렇게 하면 원하는 어떤 레이아웃 지시사항(Modifier)도 자식들에게 적용할 수 있다. 즉, 함수가 특정(내부) Modifier에 의존하지 않고 외부의 Modifer를 적용할 수 있어서 재사용이 가능하다.

또한, onCreate가 깔끔해지고 MyApp을 Preview 함수에서 그대로 호출할 수 있으므로 코드 중복도 줄일 수 있다.

아래는 지금까지의 전체 코드이다.

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            BasicsCodelabTheme {
                MyApp(modifier = Modifier.fillMaxSize())
            }
        }
    }
}

@Composable
private fun MyApp(modifier: Modifier = Modifier) {
	// onCreate에서 호출하던 코드를 분리했다
    Surface(
        modifier = modifier,
        color = MaterialTheme.colorScheme.background
    ) {
        Greeting("Android")
    }
}

@Composable
private fun Greeting(name: String) {
    Surface(color = MaterialTheme.colorScheme.primary
    ) {
        Text(text = "Hello $name!", modifier = Modifier.padding(24.dp))
    }
}

@Preview(showBackground = true)
@Composable
private fun DefaultPreview() {
    BasicsCodelabTheme {
        MyApp() // 프리뷰에 고대로 사용
    }
}

6. 행, 열 구조 만들기

컴포즈에는 3가지의 기본 레이아웃 요소가 있다: Column, Row, Box

이들은 모두 컴포즈 함수다. 따라서, 아래처럼 다른 컴포즈들을 내부에 위치시킬 수 있다.

@Composable
private fun Greeting(name: String) {
    Surface(color = MaterialTheme.colorScheme.primary) {
        Column(modifier = Modifier.padding(24.dp)) {
            Text(text = "Hello,")
            Text(text = name)
        }
    }
}

Column 내부를 보면 24dp 패딩을 적용했고, 결과는 아래와 같다.

컴포즈와 코틀린

컴포즈의 장점은 UI display(생김새)에도 코틀린을 사용할 수 있다는 것이다.

예를 들어, Column 안에서 for 문으로 컴포즈 함수를 호출하여 UI 디스플레이를 정의할 수 있다.

@Composable
fun MyApp(
    modifier: Modifier = Modifier,
    names: List<String> = listOf("World", "Compose")
) {
    Column(modifier) {
        for (name in names) {
            Greeting(name = name)
        }
    }
}

@Preview(showBackground = true, widthDp = 320)
@Composable
fun DefaultPreview() {
    BasicsCodelabTheme {
        MyApp()
    }
}

@Preview의 widthDp = 320에 주목하자. 프리뷰 전체 width가 320이 되도록 설정했다.

Modifier 연습해보기

Modifier는 매우 광범위하게 사용된다. fillMaxWidth와 padding modifiers를 사용해서 아래의 사진을 구현해보자.

코드랩에서 제시한 코드는 아래와 같다. 구현 방법은 여러 가지가 있으므로 꼭 같지 않아도 된다. 또한, Modifier에 chain을 걸어서 여러 속성을 추가할 수 있다는 점에 주목하자.

@Composable
fun MyApp(
    modifier: Modifier = Modifier,
    names: List<String> = listOf("World", "Compose")
) {
    Column(modifier = modifier.padding(vertical = 4.dp)) {
        for (name in names) {
            Greeting(name = name)
        }
    }
}

@Composable
private fun Greeting(name: String) {
    Surface(
        color = MaterialTheme.colorScheme.primary,
        modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp)
    ) {
        Column(modifier = Modifier.fillMaxWidth().padding(24.dp)) {
            Text(text = "Hello, ")
            Text(text = name)
        }
    }
}

Button 추가하기

Button은 material 패키지에서 제공되는 컴포즈 함수이며, 마지막 인자로 컴포즈 함수(보통 람다)를 받는다.

Button을 이용하여 기존 Greeting에서 아래의 사진을 구현해보자.

이것을 구현하려면 Row가 필요하다.

또한, alignEnd와 같은 속성은 없으므로 대신 weight를 활용할 수 있다. 앞에 있는 Text에 weight 1f을 설정하면, 앞의 나머지 영역은 모두 Text가 차지한다.

구현 코드는 다음과 같다.

@Composable
private fun Greeting(name: String) {

    Surface(
        color = MaterialTheme.colorScheme.primary,
        modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp)
    ) {
        Row(modifier = Modifier.padding(24.dp)) {
        	// 뒤에 배치된 Button의 크기를 제외한 모든 영역을 차지한다.
            Column(modifier = Modifier.weight(1f)) {
                Text(text = "Hello, ")
                Text(text = name)
            }
            ElevatedButton(
                onClick = { /* TODO */ }
            ) {
                Text("Show more")
            }
        }
    }
}

0개의 댓글