Android Jetpack Compose에 대해 - RecyclerView, Layout

김말이·2023년 7월 9일
0

Compose

기존의 안드로이드 개발에서는 앱의 UI를 구성하기 위해 xml을 사용하였다. 각각의 뷰에 아이디를 생성하여 이를 자바(코틀린) 파일에서 호출하여 사용하는 방식이다.
하지만 Jetpack Compose는 선언형 UI를 기반으로, 코틀린 환경에서 구성 요소들을 만들 수 있다.

Compose는 Android의 최신 권장 도구 키트로, 이미 Android Studio에서 기본 템플릿으로 사용할 수 있다.

Compose Layout

이러한 선언형 UI를 사용해본 적이 없기 때문에, ConstraintLayout을 좋아하는 나에게는 Compose로 레이아웃을 어떻게 만든다는 것인지 감이 잘 잡히지 않았다. 이에 대한 공식 문서가 있어 읽어 보았다.

@Composable
fun ArtistCard() {
    Text("Alfred Sisley")
    Text("3 minutes ago")
}

먼저 기본적으로 코드를 작성했을 때, 동일한 위치에 겹쳐서 나타나게 된다.

Row, Column

@Composable
fun ArtistCard(artist: Artist) {
    Row(verticalAlignment = Alignment.CenterVertically) {
        Image(/*...*/)
        Column {
            Text(artist.name)
            Text(artist.lastSeenOnline)
        }
    }
}


Row와 Column 함수를 이용하여, 기존의 TableLayout과 비슷한 방식으로 레이아웃을 구현할 수 있다.

Box

@Composable
fun AlignInRow() {
    Row(
        modifier = Modifier
            .size(150.dp)
            .background(Color.Yellow),
        horizontalArrangement = Arrangement.End,
        verticalAlignment = Alignment.CenterVertically
    ) {
        Box(
            Modifier
                .size(50.dp)
                .background(Color.Red)
        )
        Box(
            Modifier
                .size(50.dp)
                .background(Color.Green),
            contentAlignment = Alignment.Center
        ) {
            Box(
                Modifier
                    .size(30.dp)
                    .background(Color.Blue)
            )
        }
    }
}

Box는 요소 위에 다른 요소를 배치할 수 있다.

기존 XML에서는 중첩된 뷰를 권장하지 않았는데, Compose에서는 이러한 사용이 성능상 크게 문제가 없다고 한다.

Scroll

@Composable
fun ScrollBoxes() {
    Column(
        modifier = Modifier
            .background(Color.LightGray)
            .size(100.dp)
            .verticalScroll(rememberScrollState())
    ) {
        repeat(10) {
            Text("Item $it", modifier = Modifier.padding(2.dp))
        }
    }
}

스크롤 같은 경우, verticalScrollhorizontalScroll 수정자(modifiers)를 통해 간단하게 구현된다.

ConstraintLayout

@Composable
fun ConstraintLayoutContent() {
    ConstraintLayout {
        // Create references for the composables to constrain
        val (button, text) = createRefs()

        Button(
            onClick = { /* Do something */ },
            // Parent의 상단과 연결 후 Margin값 부여
            modifier = Modifier.constrainAs(button) {
                top.linkTo(parent.top, margin = 16.dp)
            }
        ) {
            Text("Button")
        }

        // Button의 하단과 연결 후 Margin값 부여
        Text("Text", Modifier.constrainAs(text) {
            top.linkTo(button.bottom, margin = 16.dp)
        })
    }
}


Compose에도 위와 같이 ConstraintLayout을 구성할 수 있다! 심신의 안정이 찾아왔다...

Compose는 기존 XML 방식에 비해서 어떤 점이 좋은 것인가?

코드 감소

  1. 코드의 규모가 줄어든다.
  • 가장 많은 효과를 볼 수 있는 것이 바로 RecyclerView 구현 문제이다. RecyclerView를 통해 리스트를 하나 만들기 위해서는 ViewHolder, ViewGroup, LayoutManager, RecyclerViewAdapter, ItemLayout 등등... 만들어야 할 것들이 너무 많았다. 하지만 Compose를 사용하면 LazyColumn 하나로도 충분하다.

RecyclerView

기존에 RecyclerView를 만들기 위해서는 아래와 같은 과정을 거쳐왔다.
아이템 레이아웃을 만들고, 이를 넣을 화면에 담긴 리사이클러뷰에 아이템으로 지정해 준 뒤, RecyclerView의 Adapter를 만들어준다.
Adapter 안에는 아이템과의 뷰 바인딩, 뷰홀더를 통한 데이터 바인딩 등의 작업을 하였다.

그리고 이 RecyclerView를 사용할 화면과 연결된 자바/코틀린 파일에서 RecyclerView를 선언해 주면 된다!

과정이 복잡하고 RecyclerView Adapter를 다루는 것이 까다롭기 때문에 이전에는 내가 RecyclerView를 만들어야 한다고 하면 심지어 중첩 리사이클러뷰라면 아... 막막하다... 라는 생각이 들었던 것이 사실이다. 하지만 이렇게 할 수밖에 없으니 불만 없이 열심히 했겠지만, Compose를 통해 쉽게 RecyclerView를 구현할 수 있다고 하면 말이 달라진다.

@Composable
fun SimpleColumn() {
    Column {
        repeat(100) {
            Text("Item #$it", style = MaterialTheme.typography.subtitle1)
        }
    }
}

가장 기본적인 형태로 구현된 리스트이다.

@Composable
fun LazyList() {
    // We save the scrolling position with this state
    val scrollState = rememberLazyListState()

    LazyColumn(state = scrollState) {
        item {
            Text(text = "First item")
        }

        // Add 5 items
        items(5) { index ->
            Text(text = "Item: $index")
        }

        // Add another single item
        item {
            Text(text = "Last item")
        }
    }
}

LazyList를 통해서, 이전에 구현하기 힘들었던 처음/마지막 아이템 관리가 훨씬 간편해진다.

Compose의 기능에 대해 검색해 보면서, 가장 감탄한 기능이 바로 Sticky Header이다. 이전에 이러한 레이아웃을 구현하기 위해 정말 고생했던 기억이 있는데, Compose에서는 하나의 함수로 해결된다.

@OptIn(ExperimentalFoundationApi::class)
@Composable
fun ListWithHeader(items: List<Item>) {
    LazyColumn {
        stickyHeader {
            Header()
        }

        items(items) { item ->
            ItemRow(item)
        }
    }
}

이 기능을 포함하여 스크롤 위치를 제어하고, 위치에 따른 반응을 조절할 수 있다.

직관적

  1. 상태가 명시적이다.

빠른 개발 과정

  1. Navigation, ViewModel, Coroutines와 호환된다.
  2. 미리보기를 제공하여 시간이 절약된다.

성능

  1. Material 디자인, 다크모드, 애니메이션 등의 기능을 기본적으로 지원한다.
  2. 접근성 API, 레이아웃 등이 개선되었다.

결론

Compose는 확실히 정말 멋지다... 이에 적응하기 위해서 앞으로 더 많은 공부와 적응이 필요하겠지만, 그 노력이 충분히 가치가 있고 아니 컴포즈에 적응하는 것은 이미 필수이고 새로운 기술을 마주하여 공부할 생각에 아주 설렌다. 앞으로 이보다 더 놀라운 변화들이 많이 있겠지? 라는 생각에 조금 두렵기도 하다.

참고
Compose를 사용해야 하는 이유
Jetpack Compose 기초
Compose의 레이아웃
LazyList and Grid

profile
공부해서 남주자

0개의 댓글