24.01.30 Compose 좋아요 기능 구현하기

KSang·2024년 1월 30일
0

TIL

목록 보기
45/101

이미지와 카드, 그리고 상태

이번에는 이미지를 누르면 좋아요 표시가되는걸 만들어볼꺼다

앞서 말했던 @Composable를 이용해서 만들자

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ImageCard()
        }
    }
}

@Composable
fun ImageCard() {
...

일단 setContent에 이미지 카드를 구현할 컴포즈인 ImageCard()를 넣어준다.

Card(
        modifier = Modifier
            .fillMaxWidth(0.5f)
            .padding(16.dp),
        shape = RoundedCornerShape(8.dp),
        elevation = CardDefaults.elevatedCardElevation(defaultElevation = 5.dp),
    )

Card는 xml에서 카드뷰 같은 것 이라고 보면된다,

radius를 정해주고 elevation을 넣어줬다.

        Image(
                modifier = Modifier.fillMaxSize(),
                painter = painterResource(id = R.drawable.img_winter),
                contentDescription = "설명을 작성 하는곳",
                contentScale = ContentScale.Crop
            )

Image는 이미지 뷰 같은거라고 보면 된다.

이미지는 PainterResource를 이용해 res파일에 있는 이미지를 불러와주고

contentScale = ContentScale.Crop로 CenterCrop같은 효과를 내준다.

이제 좋아요 버튼을 누를 하트 표시를 만들어보자

            Box(
                modifier = Modifier.fillMaxSize(),
                contentAlignment = Alignment.TopEnd,
            ) {
                IconButton(onClick = {  }) {
                    Icon(imageVector = Icons.Default.FavoriteBorder,
                        contentDescription = "무조건 작성해야함",
                        tint = Color.White
                    )
                }
            }

IconButton(onClick = { })을 만들었는데 저기 onClick에 변경될 로직을 넣어주면 좋아요 버튼을 구현 할 수 있다.

그리고 밑에 Image도 가능한데 Icon() 에서 imageVector를 사용해 벡터 이미지를 따로 drawable 만들지 않고 구성 할 수 있다.

fun ImageCard() {
    Card(
        modifier = Modifier
            .fillMaxWidth(0.5f)
            .padding(16.dp),
        shape = RoundedCornerShape(8.dp),
        elevation = CardDefaults.elevatedCardElevation(defaultElevation = 5.dp),
    ) {
        Box(
            modifier = Modifier.height(200.dp)
        ) {
            Image(
                modifier = Modifier.fillMaxSize(),
                painter = painterResource(id = R.drawable.img_winter),
                contentDescription = "설명을 작성 하는곳",
                contentScale = ContentScale.Crop
            )
            Box(
                modifier = Modifier.fillMaxSize(),
                contentAlignment = Alignment.TopEnd,
            ) {
                IconButton(onClick = {  }) {
                    Icon(imageVector = Icons.Default.FavoriteBorder,
                        contentDescription = "무조건 작성해야함",
                        tint = Color.White
                    )
                }
            }
        }
    }
}

image

이제 클릭했을때 좋아요 표시가 되게 구현해보자

좋아요 기능

remember

jetpack compose에선 리멤버가 중요하다고 한다.

좋아요를 눌렀는지 안눌렀는지 상태를 기억해야하는데

이때 remember를 쓴다

    val isFavorite = remember {
        mutableStateOf(false)
    }

이제 false라는 상태를 기억하는데, 이제 이 값이 바뀔때마다 UI를 갱신시켜줄거다

                IconButton(onClick = {
                    isFavorite.value = !isFavorite.value
                }) {
                    Icon(imageVector = if (isFavorite.value) {
                        Icons.Default.Favorite
                    } else {
                        Icons.Default.FavoriteBorder
                    } ,
                        contentDescription = "무조건 작성해야함",
                        tint = Color.White
                    )
                }

아까 작성한 좋아요 버튼에 조건문을 걸어주자

IconButton(onClick = {
                    isFavorite.value = !isFavorite.value
                })

그리고 onClick에 상태를 변경시켜주는 로직을 넣어주기만 하면된다

그런데 이렇게 하면 계속 value를 써줘야하는 불편함이 있는데

by를 써서 이를 해결 가능하다

    var isFavorite by remember {
        mutableStateOf(false)
    }
...
             IconButton(onClick = {
                    isFavorite = isFavorite.not()
                }) {
                    Icon(imageVector = if (isFavorite) {
                        ...

getValue와 setValue를 임포트하면 이런식으로 표현이 가능하다

image

rememberSaveable

그런데 이런식으로 구현할 경우 화면을 회전 시키는등 뷰가 파괴될때 데이터 저장이 안된다

image

컴포즈에선 rememberSaveable를 사용하면 손쉽게 해결이 가능하다

    var isFavorite by rememberSaveable {
        mutableStateOf(false)
    }

상태 파라미터로 빼주기

이미지 카드를 이렇게 빼서 컴포즈로 만들어준 이유는

컴포즈를 재사용기 위함인데 그러면

remember 가 컴포즈 안에 있으면 안된다

아키텍처 및 컴포넌트 설계의 원칙때문인데

컴포저블을 보다 범용적으로 만들어서 어떤 상태 관리 방식에도 유연하게 대응할 수 있도록 재사용성을 높이고

소유권을 상위 컴포저블이 가지게되서 흐름 제어나 상태를 다른 컴포저블에도 전달하는등 관리하기 용이해진다

그렇기 때문에 이걸 밖으로 빼내줘야하는데

        setContent {
            var isFavorite by rememberSaveable {
                mutableStateOf(false)
            }
            ImageCard(
                isFavorite = isFavorite
            ) 

setContent에서 이런식으로 선언해준다

onClick같은 경우 리사이클러뷰처럼 파라미터를 콜백으로 받게 만들자

fun ImageCard(
    isFavorite: Boolean,
    onTabFavorite: (Boolean) -> Unit,
) {
                ...

                   IconButton(onClick = {
                    onTabFavorite.invoke(isFavorite.not())
                })
                ...


    setContent {
            var isFavorite by rememberSaveable {
                mutableStateOf(false)
            }
            ImageCard(
                isFavorite = isFavorite
            ) { favorite ->
                isFavorite = favorite
            }
        }

onTabFavorite은 인보크라 생략해서 onTabFavorite(isFavorite.not())로 써주면 된다.

Modifier같은 경우도 외부에서 값을 넣어주는게 좋다고 한다. 이것도 빼주자

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

기본 값을 설정해주고

setContent {
    ...
            ImageCard(
                modifier = Modifier
                    .fillMaxWidth(0.5f)
                    .padding(16.dp),

이런식으로 외부에서 값을 넣어주면 된다

0개의 댓글