Beeeam

컴포즈 공부 4일차 (code lab) 본문

Android

컴포즈 공부 4일차 (code lab)

Beamjun 2023. 12. 1. 19:45

오늘은 코드랩를 진행하면서 공부하였다. 코드랩은 밑의 링크에서 진행해 볼 수 있다.

https://developer.android.com/codelabs/jetpack-compose-basics?hl=ko#1

 

Jetpack Compose 기초  |  Android Developers

이 Codelab에서는 Compose의 기본사항을 알아봅니다.

developer.android.com

몇 가지 빼고는 다 앞에서 공부 했던 것들이라서 복습하는 느낌이 많이 들었다.

 

기본적으로 컴포즈로 프로젝트를 생성하면 다음과 같은 형식의 코드를 볼 수 있다.

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            BasicsCodelabTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                  modifier = Modifier.fillMaxSize(),
                  color = MaterialTheme.colorScheme.background
                ) {
                    Greeting("Android")
                }
            }
        }
    }
}

Surface의 본문의 Greeting 컴포저블이 뷰 컴포넌트를 정의하고 있고 이를 Activity에서 실행하는 것이다. 컴포즈는 사용자가 정의한 뷰를 미리보기 할 수 있는 기능을 지원한다. 미리보기 기능은 다음의 코드를 선언하면 사용할 수 있다.

@Preview(showBackground = true, name = "Text preview")
@Composable
fun DefaultPreview() {
    BasicsCodelabTheme {
        Greeting(name = "Android")
    }
}

 

modifier 인자를 사용하여 컴포넌트의 위치, 크기, 패딩 등을 정의할 수 있다.

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

위와 같이 선언하면 Text의 모든 면(Top, Bottom, Start, End)에 패딩이 적용된 것을 확인할 수 있다.

 

Row와 Column을 사용하여 레이아웃에 컴포넌트들을 정렬할 수 있다.

modifier의 fillMaxWidth을 사용하면 해당 컴포넌트의 가로 크기를 화면에 꽉 채울 수 있다. 그리고 weight를 사용하면 컴포넌트의 크기에 가중치를 줘서 가중치가 없는 컴포넌트를 밀어내서 사용할 수 있는 모든 공간을 사용할 수 있다.

이를 이용하여 다음과 같은 뷰를 구현할 수 있다.

@Composable
fun Greeting(text: String) {
    Surface(
        color = MaterialTheme.colorScheme.primary,
        modifier = Modifier.padding(vertical = 4.dp, horizontal = 8.dp),
    ) {
        Row(modifier = Modifier.fillMaxWidth().padding(24.dp)) {
            Column(Modifier.weight(1f)) {
                Text(text = "Hello")
                Text(text = text)
            }
            ElevatedButton(
                onClick = { /*TODO*/ },
            ) {
                Text(text = "Show more")
            }
        }
    }
}

컴포넌트들을 가로로 정렬하는 Row에 fillMaxWidth를 사용하여 화면의 가로를 꽉 채우게 한 다음 텍스트를 열로 정렬하는 Column에 가중치를 줘서 가중치가 없는 버튼을 오른쪽으로 밀어내서 위와 같은 컴포넌트를 구현할 수 있다.

컴포즈의 상태는 remember 키워드를 사용하여 mutableStateOf로 감싸서 만들 수 있다.

 

ex)

val expanded = remember { mutableStateOf(false) }
// 또는
val expanded by remember { mutableStateOf(false) }

번외)

상태는 위와 같이 두 가지 방법으로 표현이 가능하다. 그럼 둘의 차이점은 뭘까
= 연산자를 사용하여 상태를 만들면 해당 객체에 직접 접근할 수 없다.
하지만 by 키워드를 사용하여 상태를 만들면 해당 객체에 직접 접근할 수 있다.

= 연산자를 사용하면 expanded.value = true 이 처럼 접근해야 한다.
by 키워드를 사용하면expanded = true 이 처럼 접근할 수 있는 차이가 있다.

 

컴포즈에서 상태를 관리하는 것은 중요하다. 한 컴포저블에서 많은 상태를 관리하게 되면 해당 컴포저블은 재사용성이 떨어진다. 이 때문에 상태 호이스팅을 사용한다. 이는 상태를 들어올린다는 뜻으로 여러 컴포저블들이 사용하거나 수정하는 상태는 상위 컴포저블에서 관리하도록 하는 것이다.

@Composable
fun OnboardingScreen(modifier: Modifier = Modifier) {
    // TODO: This state should be hoisted
    var onBoardingState by remember { mutableStateOf(true) }

    Column(
        modifier = modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally
    ) {
        Text("Welcome to the Basics Codelab!")
        Button(
            modifier = Modifier.padding(vertical = 24.dp),
            onClick = { onBoardingState= false }
        ) {
            Text("Continue")
        }
    }
}

위의 온보딩 컴포저블을 onBoardingState 상태의 값에 따라 보여줄 지 말지를 결정해야한다. 이를 구현하기 위해 다음과 같이 상위 컴포저블에서 상태를 관리하게 하였다.

@Composable
fun MyApp() {
    var onBoardingState by remember { mutableStateOf(true) }

    if (onBoardingState) {
        OnboardingScreen(onContinueClick = { onBoardingState = false })
    } else {
        Greetings()
    }
}

@Composable
fun OnboardingScreen(onContinueClick: () -> Unit) {
    Column(
        modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally,
    ) {
        Text("Welcome to the Basics Codelab!")
        Button(
            modifier = Modifier.padding(vertical = 24.dp),
            onClick = onContinueClick,
        ) {
            Text("Continue")
        }
    }
}

이 처럼 상위 컴포저블에서 상태를 관리하면서 수정하고자 하면 하위 컴포저블에 람다로 전달하여 상태 호이스팅을 하면 컴포저블의 재사용성을 높일 수 있다.

lazyColumn 만들기

이건 코드랩 진행하면서 처음 접한 개념이다.

xml로 스크롤을 할 수 있는 리스트를 구현할 때는 RecyclerView를 사용하였는데 컴포즈에서는 lazyColumn을 사용하여 구현할 수 있다.

@Composable
fun Greetings() {
    val names = List(100) { "$it" }
    Surface(modifier = Modifier.padding(vertical = 4.dp)) {
        LazyColumn {
            items(items = names) { name ->
                Greeting(text = name)
            }
        }
    }
}

LazyColumn 본문에 items의 인자로 사용하고자 하는 리스트를 넣고 본문에 리스트의 아이템 뷰에 해당하는 컴포저블을 넣어주면 구현된다.

 

애니메이션은 animateDpAsState 컴포저블을 사용하여 구현할 수 있다. 클릭했을 때 펼쳐줄 크기를 정의한 변수를 다음과 같이 정의하면 애니메이션을 적용할 수 있다.

val extraPadding by animateDpAsState(
    if (expanded) 48.dp else 0.dp,
    animationSpec = spring(
        dampingRatio = Spring.DampingRatioMediumBouncy,
        stiffness = Spring.StiffnessLow
    )
)

spring 말고 다른 애니메이션들도 적용이 가능하다.

 

위의 뷰에서 버튼의 텍스트를 “Show more” 형식으로 정의해놨다. 이런 하드코딩은 좋지 않기 때문에 이를 app/src/res/values/strings.xml 파일에 다음과 같이 정의하고 사용하는게 좋다.

<string name="show_less">Show less</string>
<string name="show_more">Show more</string>

// 사용
stringResource(R.string.show_less)
stringResource(R.string.show_more)

 

작성 코드

https://github.com/BEEEAM-J/compose_code_lab

 

GitHub - BEEEAM-J/compose_code_lab

Contribute to BEEEAM-J/compose_code_lab development by creating an account on GitHub.

github.com

 

'Android' 카테고리의 다른 글

LazyColumn  (1) 2024.01.23
컴포즈 내비게이션  (2) 2023.12.26
컴포즈 공부 3일차  (1) 2023.11.28
컴포즈 공부 2일차  (1) 2023.11.26
컴포즈 공부 1일차 (코드 형식 및 연습)  (1) 2023.11.26