본문 바로가기
Android/Jetpack Compose App

JETPACK COMPOSE: 터치하면 돈이 올라가는 앱을 만들어보자 - 3

by 개발자J의일상 2022. 1. 21.
반응형

Composable의 상태

 

구성 가능한(Composable) 함수 remember composable을 사용하여 메모리에 단일 객체를 저장할 수 있습니다.

remember에 의해 계산된 값은 초기 컴포지션 중에 컴포지션에 저장되고 저장된 값은 리컴포지션 중에 반환됩니다.

remember는 변경 가능한 객체뿐만 아니라 변경할 수 없는 객체를 저장하는데 사용할 수 있습니다.

 

* remember는 객체를 컴포지션에 저장하고, remember를 호출한 컴포저블이 컴포지션에서 삭제되면 그 객체를 잊습니다.

interface MutableState<T> : State<T> {
    override var value: T
}

MutableState는 State를 상속하고 interface입니다. 안에 멤버변수로 value를 가지고 있습니다.

 

value가 변경되면 value를 읽는 구성 가능한 함수의 re-composition이 예약됩니다.

Box안에 Text의 경우 moneyCounter가 변경될때마다 Text가 재구성됩니다.

 

Composable에서 MutableState 객체를 선언하는 데는 세 가지 방법이 있습니다.

 

  • val mutableState = remember { mutableStateOf(default) }
  • var value by remember { mutableStateOf(default) }
  • val (value, setValue) = remeber { mutableStateOf(default) }

이러한 선언은 동일한 것이고 서로 다른 용도의 상태를 사용하기 위한 구문으로 제공됩니다. 작성 중인 컴포저블에서 가장 읽기 쉬운 코드를 생성하는 선언을 선택하면 됩니다.

 

지난 시간에는 두 번째 방법으로 moneyCount를 선언했는데요

var moneyCounter by remeber { mutableStateOf(0) }로 선언을 했었는데요!

 

JETPACK COMPOSE: 터치하면 돈이 올라가는 앱을 만들어보자 - 2

 

JETPACK COMPOSE: 터치하면 돈이 올라가는 앱을 만들어보자 - 2

지난 시간 리뷰 JETPACK COMPOSE: 터치하면 돈이 올라가는 앱을 만들어보자 - 1 JETPACK COMPOSE: 터치하면 돈이 올라가는 앱을 만들어보자 - 1 지난 시간 리뷰 Android studio 기본 예제로 알아보는 Jetpack Comp..

mypark.tistory.com

 

이번 시간에는 첫 번째 방법으로 선언하는 방법을 살펴보겠습니다.

 

fun CreateCircle() {
//    var moneyCounter by remember {
//        mutableStateOf(0)
//    }
    var moneyCounter = remember { // [1]
        mutableStateOf(0)
    }
    Card(modifier = Modifier
        .padding(3.dp)
        .size(100.dp)
        .clickable {
            //moneyCounter += 1
            moneyCounter.value += 1 // [2]
            //Log.d("Counter", "CreateCircle : $moneyCounter")
            Log.d("Counter", "CreateCircle : ${moneyCounter.value}")
        }
        ,
        shape = CircleShape,
        elevation = 4.dp
    ) {
        Box(contentAlignment = Alignment.Center) {
            //Text(text = "Tap $moneyCounter", modifier = Modifier)
            Text(text = "Tap ${moneyCounter.value}", modifier = Modifier) // [3]
        }

    }
}

다시 선언된 moenyCounter와 값을 1씩 증가시키는 부분에 주목해봅시다

  • [1] by가 없이 = remember로 moneyCounter를 선언해주었습니다.
  • [2] moneyCounter +=1 하는 게 아니라 moneyCounter.value += 1을 해주어야 정상 동작됩니다. 위에 살펴봤듯이 mutableStateOf()는 value를 멤버 변수로 갖습니다.
  • [3] moneyCounter를 출력할 때도 value를 출력해야 숫자가 출력됩니다.

이제 tap을 누를 때마다 달러가 변화하도록 하고 싶은데 어떻게 해야 될까요?

@Composable
fun MyApp() {
    Surface(
        modifier = Modifier
            .fillMaxHeight()
            .fillMaxWidth(),
        color = Color(0xFF4666d0)) {
        Column(
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        )
        {
            Text(text = "$100", style = TextStyle(
                color = Color.White,
                fontSize = 35.sp,
                fontWeight = FontWeight.ExtraBold
            ))
            Spacer(modifier=Modifier.height(130.dp))
            CreateCircle()

        }
    }
}

 

달러(현재 $100)는 Myapp()의 Text에 있고 Tap 자체는 CreateCircle()에서 호출되는 Composable이기 때문에 moneyCounter의 위치를 MyApp()으로 변경해야 두 곳에서 사용할 수 있게 됩니다.

 

@Composable
fun MyApp() {
    var moneyCounter = remember {
        mutableStateOf(0)
    }
    Surface(
        modifier = Modifier
            .fillMaxHeight()
            .fillMaxWidth(),
        color = Color(0xFF4666d0)) {
        Column(
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        )
        {
            Text(text = "$${moneyCounter.value}", style = TextStyle(
                color = Color.White,
                fontSize = 35.sp,
                fontWeight = FontWeight.ExtraBold
            ))
            Spacer(modifier=Modifier.height(130.dp))
            CreateCircle(moneyCounter = moneyCounter.value) { newValue ->
                moneyCounter.value = newValue + 1
            }
        }
    }
}

 

우선 moneyCounter의 위치를 옮기고 Text에서는 moneyCounter의 value값을 출력하도록 수정합니다.

그리고 CreateCircle에 moneyCounter.value를 넘기고 function이 call 될 때마다 수행할 lamda function을 만드는데 그냥 넘어온 값(newValue)에 +1을 한 값을 다시 moneyCounter.value에 저장합니다.

 

@Composable
fun CreateCircle(moneyCounter: Int = 0, updateMoneyCounter: (Int) -> Unit) {
    Card(modifier = Modifier
        .padding(3.dp)
        .size(100.dp)
        .clickable {
            //moneyCounter += 1
            updateMoneyCounter(moneyCounter)
            Log.d("Counter", "CreateCircle : $moneyCounter")
        }
        ,
        shape = CircleShape,
        elevation = 4.dp
    ) {
        Box(contentAlignment = Alignment.Center) {
            Text(text = "Tap $moneyCounter", modifier = Modifier)
        }

    }
}

 

CreateCircle에 두 번째 매개변수로 lamda function이 들어가 있는데 Int를 받아서 Unit를 return 합니다. 

.clickable(클릭이 되면) updateMoneyCounter에 현재 받은 moneyCounter를 인자로 넘겨줍니다.

 

 

이제 클릭되는 Count만큼 달러도 update가 되는 것을 볼 수 있습니다!

이렇게 구성을 하면 mutableState를 넘기는 게 아니라 Int만 넘기면 되기 때문에 간단합니다.

 

이렇게 구성을 하면 또 무엇을 할 수 있을까요?

@Composable
fun MyApp() {
    var moneyCounter = remember {
        mutableStateOf(0)
    }
    Surface(
        modifier = Modifier
            .fillMaxHeight()
            .fillMaxWidth(),
        color = Color(0xFF4666d0)) {
        Column(
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        )
        {
            Text(text = "$${moneyCounter.value}", style = TextStyle(
                color = Color.White,
                fontSize = 35.sp,
                fontWeight = FontWeight.ExtraBold
            ))
            Spacer(modifier=Modifier.height(130.dp))
            CreateCircle(moneyCounter = moneyCounter.value) { newValue ->
                moneyCounter.value = newValue + 1
            }

            if(moneyCounter.value > 10) {
                Text("Over $10 !!")
            }
        }
    }
}

 

if문을 넣어서 원하는 값이 되면 Composable을 추가할 수 있습니다.

 

moneyCounter.value가 10보다 클 때만 위와 같이 Text를 출력하게 할 수도 안 할 수도 있게 됩니다.

300x250

댓글