[Android] - Jetpack Compose Recyclerview : Lazy Column (Part 1)

김민주·2022년 7월 2일
1

Jetpack-compose

목록 보기
1/2
post-thumbnail

Jetpack Compose
Jetpack Compose는 새롭게 등장한 안드로이드 네이티브 UI 개발 도구다. 기존의 xml을 대체한 선언형(declarative) UI로 UI 개발을 간소화하고 가속화한 도구이다. 즉, 고성능 발휘와 맞춤 레이아웃을 쉽게 작성하기 위해 만들어졌다.

0. 초기 세팅

새 프로젝트 생성

상단바 File > New > New Project를 들어가 Empty Compose Activity를 선택하여 새 안드로이드 프로젝트를 만듭니다.

앱 이름과 package name은 자유롭게 적으면 됩니다. 보통 package는 주소 거꾸로 + 앱이름 조합을 사용하는데, 저는 주소가 없으므로 그냥 제 github 이름과 앱이름 조합으로 만들었습니다.

Finish를 누르면 프로젝트 생성이 완료 됩니다.

왼쪽 split을 누르면 preview 화면을 볼 수 있습니다. preview 화면은 코드가 바뀌면 build&refresh 버튼을 눌러줘야 새로고침이 됩니다.

참고로 jectpack-compose version은 Gradle > build.gradle(Project)에서 확인하실 수 있습니다. compose-version이 jetpack-compose version이고, project 생성할 때 자동으로 build 된 것을 확인하실 수 있습니다.

1. 구조 잡기

이제 MainActivity.kt 파일에서 Greeting() 함수를 지우고 recyclerview를 구현하기 위해 필요한 함수들을 만들도록 하겠습니다.

1.1. 먼저 MyApp() 함수를 만들도록 하겠습니다.

@Composable
fun MyApp() {
    Scaffold(
        backgroundColor = MaterialTheme.colors.background
    ) {
        RecyclerViewContent()
    }
}

Scaffold로 감싸 RecyclerViewContent()를 넣어줬습니다.
RecyclerViewContent()는 실질적인 Recyclerview, 즉 데이터 목록을 보여주는 함수입니다. 이를 구현하는 것이 오늘의 과제겠죠?

🤔 Scaffold란?

기본 material design 레이아웃 구조로 UI를 구현할 수 있도록 
Jetpack-compose에서 제공해주는 툴입니다.

따라서 처음 안드로이드 screen 구조를 잡을 때 좋습니다. 

Scaffold는 TopAppBar, BottomAppBar, FloatingActionButton, Drawer 등 
가장 일반적인 상위 머티리얼 구성요소용 슬롯을 제공합니다. 
BottomNavigation을 사용이 궁금하다면 아래의 글을 참고해주세요😉

> BottomNavigation 사용법 보러가기


1.2. MyApp()을 onCreate 함수의 setContent의 ~Theme 아래에 넣어주세요.

override fun onCreate(savedInstanceState: Bundle?<) {
        super.onCreate(savedInstanceState)
        setContent {
            RecyclerviewTheme {
                MyApp()
            }
        }
    }

Preview로 UI가 바뀌는 걸 보면서 구현하려면 DefaultPreview()에도 MyApp()을 넣어줘야겠죠?


이제 본격적으로 RecyclerView를 만들도록 하겠습니다. RecyclerViewContent() 함수에서 구현하면 됩니다.

그런데, RecyclerView를 구현하기 위해선 그 안에 들어갈 데이터와 데이터 형식이 필요합니다. 따라서 먼저 데이터 작업을 진행하도록 하겠습니다!

2. 데이터 생성 및 초기화

2.1. 데이터 클래스 생성

저는 강아지 목록을 recylerview 형식을 보여줄 것입니다. 데이터는 '가아지 이름, 강아지 사진, 내용'으로 구성했습니다.

우측 마우스 클릭 > New > Kotlin class > Data class 를 선택하여 Puppy data class를 만들어주면 됩니다.

data class Puppy (
    val name: String,
    val image: Int,
    val content: String
)

2.2 데이터 list 만들기
화면에 보여줄 데이터 list가 필요합니다. 저는 아래 사이트에서 강아지 사진을 구해서 강아지 클래스에 강아지 사진을 넣어줬습니다. (무료 사이트)
https://www.pexels.com/search/puppy/

다운 받은 사진을 ctrl+c 한 다음 res > drawable에 ctrl+v 하면 project에 들어갑니다.

이제 puppyList를 만들어봅시다. DataProvider object을 만들어 puppyList를 만들어주면 됩니다.

object DataProvider {
    val puppyList = listOf(
        Puppy("코코", R.drawable.puppy1, "코코코코코 코 코코코 입!"),
        Puppy("보리", R.drawable.puppy2, "보리 보리 쌀"),
        Puppy("초코", R.drawable.puppy3, "초코송이 먹고싶당"),
        Puppy("콩이", R.drawable.puppy4, "나는 콩을 안좋아하지만 콩이는 좋아!"),
        Puppy("사랑이", R.drawable.puppy5, "사랑이 덕분에 집에 사랑이 가득~"),
        Puppy("달이", R.drawable.puppy6, "달 달 무슨 달 쟁반같이 둥근 달"),
        Puppy("별이", R.drawable.puppy7, "별.. 별.. 별.."),
        Puppy("해피", R.drawable.puppy8, "암 쏘 해피~~~~ "),
        Puppy("마루", R.drawable.puppy9, "마루야.. 가지마.."),
        Puppy("까미", R.drawable.puppy10, "난 영어 이름도 있어. Kami")
    )
}

3. List UI 구현

이제 RecyclerViewContent()를 구현할 차례입니다. Recyclerview는 스크롤 할 때 view를 recycle(재활용) 하는 view였습니다. Jetpack Compose에도 이와 같은 기능을 하는 Lazy Column 이 존재합니다.

Lazy Column 말 그대로 세로로 항목들을 보여주는데, lazy가 붙어서 view를 활용할 수 있게 한 것입니다.


list를 보여주기 위해선 list와 각 항목의 내용이 필요합니다. 이 두 항목을 아래 코드와 같이 Lazy Column에 넣어주면 됩니다.

@Composable
fun RecyclerViewContent() {
    val puppies = remember { DataProvider.puppyList }
    LazyColumn(contentPadding = PaddingValues(16.dp, 8.dp)) {
        items(
            items = puppies,
            itemContent = { PuppyListItem(it) }
        )
    }
}

LazyColumn 대괄호 안의 items()가 LazyColumn에서 제공하는 해당 column에 list를 추가해주는 함수입니다. items 함수의 인자로는 items와 itemContent 등이 올 수 있습니다.

items에는 list를 넣어주면 되고, itemContent는 각 항목을 어떻게 보여줄 지를 넣어주면 됩니다.

remember는 initial composition 했을 때 저장한 값을 recomposition(상태가 변했을 때)도 데이터 그대로 사용할 수 있게 도와주는 키워드입니다.

사실 여기서 puppies에 remember keyword를 붙여서 list를 만들 필요는 없었습니다. 하지만 실제 list가 변할 일이 있을 땐 많이 사용한다고 하니 미리 알아두면 좋을 것 같아 사용해봤습니다. remember에 대해서 더 자세히는 @Composable 생명주기와 함께 추후에 다루도록 하겠습니다.

4. 각 항목(item) 구현

먼저 저는 강아지 이름과 내용을 세로 방향으로 보여줄 것입니다. 따라서 Column 안에 Text 두 항목을 넣으면 되겠죠?

@Composable
fun PuppyListItem(puppy: Puppy) {
    Column{
        Text(text = puppy.name, style = Typography.h6)
        Text(text = puppy.content, style = Typography.caption)
    }
}

build&refresh를 통해 DefaultPreview를 확인해보면 list가 만들어진 것을 확인할 수 있습니다!

이제 왼쪽에 사진도 넣어보도록 하겠습니다. 사진과 두 text가 포함된 content가 가로로 놓이면 좋을 것 같습니다. 따라서 Row를 사용하여 둘을 배치해보도록 하겠습니다.

@Composable
fun PuppyListItem(puppy: Puppy) {
    Row {
        PuppyImage(puppy = puppy)
        Column {
            Text(text = puppy.name, style = Typography.h6)
            Text(text = puppy.content, style = Typography.caption)
        }
    }
}

이 때 Image를 넣는 코드는 길기 때문에 따로 함수를 만들어 넣도록 하겠습니다.

@Composable
fun PuppyImage(puppy: Puppy) {
    Image(
        painter = painterResource(id = puppy.image), 
        contentDescription = null,
        contentScale = ContentScale.Crop,
        modifier = Modifier
            .padding(8.dp)
            .size(84.dp)
            .clip(RoundedCornerShape(CornerSize(16.dp)))
    )
}

만약 앱을 실행시켰을 때 앱이 죽고 logcat에 들어갔을때 아래와 같은 에러가 뜬다면 image 크기를 줄여줘야 합니다. 앱 사진은 고화질일 필요가 없기 때문에 저는 가장 쉬운 방법인 그림판에서 크기 조정해 해결했습니다.

이제 아래 사진과 같이 preview가 나오는 것을 확인하실 수 있습니다!

5. 보기 좋게 Modifier & Card

추가적으로 Modifier를 넣어 보기 좋게 만들어봤습니다. Modifier는 Preview에서 바로바로 확인가능하므로 이것 저것 해보면서 예쁘게 만들어주시면 됩니다.

@Composable
fun PuppyListItem(puppy: Puppy) {
    Row {
        PuppyImage(puppy = puppy)
        Column(
            modifier = Modifier
                .padding(8.dp)
                .align(Alignment.CenterVertically)
        ) {
            Text(text = puppy.name, style = Typography.h6)
            Text(text = puppy.content, style = Typography.caption, modifier = Modifier.padding(0.dp,2.dp))
        }
    }
}

이제 마지막으로 테두리를 만들어보도록 하겠습니다. 저는 테두리를 만들기 위해 Card를 사용하였습니다.

@Composable
fun PuppyListItem(puppy: Puppy) {
    Card(
        modifier = Modifier.fillMaxWidth().padding(0.dp, 12.dp),
        elevation = 4.dp,
        shape = RoundedCornerShape(corner = CornerSize(16.dp)),
        border = BorderStroke(1.dp, MaterialTheme.colors.primary)
    ) {
        Row {
            PuppyImage(puppy = puppy)
            Column(
                modifier = Modifier
                    .padding(8.dp)
                    .align(Alignment.CenterVertically)
            ) {
                Text(text = puppy.name, style = Typography.h6)
                Text(text = puppy.content, style = Typography.caption, modifier = Modifier.padding(0.dp,2.dp))
            }
        }
    }
}


완성된 화면의 preview입니다!

6. 완성

처음 봤던 gif 화면이 완성되었습니다🥳
참고로 lazy column은 스크롤을 지원하여 길이가 길어지면 자동 스크롤됩니다 :)


더 자세히보기

완전한 코드는 github에 올려놓았습니다. 확인하고 싶으신 분들은 아래를 클릭해주세요 :)

🌼더 자세한 코드 보러가기


참고 자료

https://developer.android.com/jetpack/compose/layout?hl=ko
https://www.waseefakhtar.com/android/recyclerview-in-jetpack-compose/
https://developer.android.com/jetpack/compose/state?hl=ko

profile
즐거운 개발자 김민주입니다🙂

2개의 댓글

comment-user-thumbnail
2022년 7월 3일

이미지 가져올때 버벅이거나 오류 뜰경우 Glide 로 CenterCrop( 화질 크기 ) 하시면 됩니다!!
글 잘보고 갑니다 !!

1개의 답글