Group Study (2023-2024)/Android 심화

[Android 심화] 1주차 스터디 - Jetpack Compose 입문(1)

푸리우 2023. 11. 6. 21:01

Jetpack Compose란?

Android 앱의 사용자 인터페이스(UI)를 구축하는 데 사용되는 UI 라이브러리

 

1. Column, Row, Text

Column: 수직 방향으로 UI 요소를 배치하는 레이아웃 컴포저

Row: 수평 방향으로 UI 요소를 배치하는 레이아웃 컴포저

Text: 텍스트를 화면에 표시하기 위한 Composable

Modifier: Composable 요소의 스타일, 크기, 여백, 패딩 등을 제어하기 위한 도구

Arrangement(정렬) 및 Alignment(배치) :

  • Arrangement와 Alignment는 Composable 요소의 정렬 및 간격을 조절하는 열거형이다.
  • Arrangement는 컨테이너의 수평 방향 배치, Alignment은 컨테이너의 수직 방향 정렬과 관계있다.

출처: https://semicolonspace.com/jetpack-compose-alignment-arrangement/
왼쪽이 Column의 Arrangement, 오른쪽이 Row의 Arrangement

위 화면에 대한 코드는 아래와 같다.

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Column(
                modifier=Modifier
                    .fillMaxSize()
                    .background(color= Color.Yellow)
                    .padding(16.dp),
                horizontalAlignment=Alignment.CenterHorizontally,
            ){
                Text("Hello")
                Text("world")
            }
        }
    }
}

 

2. Composable, Preview

Composable: UI 요소를 생성하고 업데이트하는 Kotlin 함수

Preview: Composable 함수를 시각적으로 미리 보고 테스트하는 데 사용된다.

@Composable
fun Greeting(name: String) {
    Text(text = "Hello $name!")
}

@Preview(showBackground = true)
@Composable
fun DefaultPreview() {
    Week1Theme {
        Greeting("Android")
    }
}

 

3. Box

Box: 여러 Composable 요소를 겹쳐 놓고 배치하는 데 사용되는 컨테이너

  • Box는 alignment 및 contentAlignment 매개변수를 통해 요소의 정렬과 위치 조정을 제어할 수 있다.
  • alignment: Box 자체를 상위 레이아웃에서 정렬, contentAlignment는 내부 요소를 정렬

Box 안에 (1) Hello 텍스트+(2) Welcome 텍스트가 들어 있는 Box를 배치하였다.

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            Box(
                modifier= Modifier
                    .background(color = Color.Green)
                    .fillMaxWidth()
                    .height(200.dp)
            ){
                Text("Hello")
                Box(
                    modifier= Modifier
                        .fillMaxSize()
                        .padding(16.dp),
                    contentAlignment = Alignment.BottomEnd,
                ){
                    Text("Welcome")
                }
            }
        }
    }
}

 

4. 리스트, LazyColumn

위와 같이 여러 요소들을 리스트 형식으로 보여주고 싶을 때 Column을 사용한다.

setContent {
            val scrollState = rememberScrollState()

            Column(
                modifier=Modifier
                    .background(color=Color.Green)
                    .fillMaxWidth()
                    .verticalScroll(scrollState)
            ){
                for (i in 1..50){
                    Text("글씨 $i")
                }
            }
        }

LazyColumn은 RecyclerView와 유사한 원리로 동작하며, 대규모 목록을 처리할 때 더 나은 성능과 메모리 관리를 제공한다. 따라서 목록이 크고 동적으로 변경될 가능성이 있는 경우 Column 대신 LazyColumn을 사용하는 것이 좋다.

다음 코드는 LazyColumn을 사용하였다.

 

setContent {
            LazyColumn(
                modifier=Modifier
                    .background(color=Color.Green)
                    .fillMaxWidth(),
                contentPadding=PaddingValues(16.dp),
                verticalArrangement=Arrangement.spacedBy(8.dp)
            ){
                item{
                    Text("Header")
                }
                items(50){index->
                    Text("글씨 $index")
                }
                item{
                    Text("Footer")
                }
            }
}

 

5. Image, Card

Image: 이미지를 표시하기 위한 Composable

  • painter 매개변수를 사용하여 이미지 리소스나 이미지 로더로부터 가져온 이미지를 설정할 수 있다.
  • contentDescription을 사용하여 이미지의 대체 텍스트를 설정하여 접근성을 향상시킬 수 있다.

Card: 컨테이너 Composable로, 주어진 내용을 둘러싸고 음영을 추가하여 마테리얼 디자인의 카드 요소를 생성할 수 있다.

  • Card 내부에 다른 Composable 요소를 배치할 수 있으며, 주로 카드 뷰로 레이아웃을 디자인하는 데 사용된다.
  • modifier를 사용하여 카드의 스타일, 모양 및 크기를 조정할 수 있다.

위와 같은 화면을 만들기 위한 코드는 아래와 같다.

@Composable
fun ImageCard() {
    var isFavorite by rememberSaveable{
        mutableStateOf(false)
    }

    Card(
        modifier= Modifier
            .fillMaxWidth(0.5f)
            .padding(16.dp),
        shape= RoundedCornerShape(8.dp),
        elevation=5.dp,
    ){
        Box(
            modifier=Modifier.height(200.dp),
        ){
            Image(
                painter = painterResource(id=R.drawable.sookmyung),
                contentDescription = "sookmyung",
                contentScale= ContentScale.Crop,
            )
            Box(
                modifier=Modifier.fillMaxSize(),
                contentAlignment=Alignment.TopEnd
            ){
                IconButton(onClick={
                    isFavorite=!isFavorite
                }){
                    Icon(
                        imageVector=if (isFavorite) Icons.Default.Favorite else Icons.Default.FavoriteBorder,
                        contentDescription="favorite",
                        tint=Color.Red
                    )
                }
            }
        }
    }
}

 

위 코드에서 ImageCard를 재사용하기 위해서 isFavorite과 modifier를 외부로 뺀다.

전체 코드는 아래와 같다.

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            var isFavorite by rememberSaveable{
                mutableStateOf(false)
            }
            ImageCard(
                modifier=Modifier
                    .fillMaxWidth(0.5f)
                    .padding(16.dp),
                isFavorite=isFavorite,
            ){
                favorite->
                isFavorite=favorite
            }
        }
    }
}

@Composable
fun ImageCard(
    modifier:Modifier=Modifier,
    isFavorite:Boolean,
    onTabFavorite:(Boolean)->Unit,
) {

    Card(
        modifier= modifier,
        shape= RoundedCornerShape(8.dp),
        elevation=5.dp,
    ){
        Box(
            modifier=Modifier.height(200.dp),
        ){
            Image(
                painter = painterResource(id=R.drawable.sookmyung),
                contentDescription = "sookmyung",
                contentScale= ContentScale.Crop,
            )
            Box(
                modifier=Modifier.fillMaxSize(),
                contentAlignment=Alignment.TopEnd
            ){
                IconButton(onClick={
                    onTabFavorite(!isFavorite)
                }){
                    Icon(
                        imageVector=if (isFavorite) Icons.Default.Favorite else Icons.Default.FavoriteBorder,
                        contentDescription="favorite",
                        tint=Color.Red
                    )
                }
            }
        }
    }
}

 

참고자료

https://www.youtube.com/playlist?list=PLxTmPHxRH3VV8lJq8WSlBAhmV52O2Lu7n

https://semicolonspace.com/jetpack-compose-alignment-arrangement/

https://nosorae.tistory.com/entry/AndroidCompose-%ED%97%B7%EA%B0%88%EB%A0%A4%EC%84%9C-%EB%94%B1-%EC%A0%95%EB%A6%AC%ED%95%98%EB%8A%94-Compose-%EC%A0%95%EB%A0%ACAlignment%EA%B3%BC-%EB%B0%B0%EC%B9%98Arrangement