티스토리 뷰

공부/ComposeCamp 2022

unit 2 : 레몬에이드 앱 만들기

데자와 맛있다 2022. 11. 27. 20:10

-공부내용 필기-

 

  • Column 가로, 세로 중앙 배치 (이 Column외 다른 컴포저블 없는경우)
Column(
        modifier = Modifier.fillMaxWidth(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {}
  • Button 컴포저블은 기본적으로 보라색이다, Button 내부에 Image를 넣어 이미지가 있는 버튼을 만들수있지만 기본적으로 적용되는 보라색이 배경에 깔리게된다
  • 클릭 가능한 컴포저블 만들기
    • Button외 다른 컴포저블을 클릭할수있게 만드려면 해당 컴포저블에 clickable 수정자를 지정한다
Image(
            painter = painterResource(drawableResourceId),
            contentDescription = stringResource(contentDescriptionResourceId),
            modifier = Modifier
                .wrapContentSize()
                .clickable(
                    onClick = onImageClick
                )
               
        )
  • IconButton을 사용해도 이미지가 있는 버튼을 만들 수 있다
  • 테두리는 modifier에 border를 주면됨
IconButton(
            onClick = {
                
            },
            modifier = Modifier.border(//테두리
                BorderStroke(2.dp, Color(105,205,216)),
                shape= RoundedCornerShape(4.dp)
            )
        ) {
            Image(
                painter = imageId,
                contentDescription = descriptionId,
            )
        }
  • 리컴포지션= 컴포저블이 다시 실행, 새로고침
    • 리컴포지션이 되면 원래 이전에 있던 컴포저블 내부 변수값은 리셋된다
    • 이전 컴포저블 내부 변수를 유지하려면 메모리에 저장해야한다 이때 remember 컴포저블을 사용함
    • mutableStateOf를 컴포저블안에서 사용해 변수를 만들면 이 변수값이 변경될때 리컴포지션이 되도록 계속 관찰하게 된다(By using mutableStateOf values in a composable function, variables can be made into observables that schedule a recomposition when their value is changed.)
  • 클릭 함수를 만들때 로직과 이미지 표시 부분을 나눈다면 로직에서 클릭 함수를 만들고 이미지 표시 컴포저블로 클릭 함수를 람다로 보내줄수있다
    • 람다식을 처음 정의할땐 아래처럼
var onClickFun: ()->Unit={
        stageNum++
        tabNum=(2..4).random()
    }
    
//var 변수명: 람다식 타입={바디}
//람다식 타입은 (매개변수들)->반환형
  • 람다식에 다른 식을 넣을땐 처음 정의할때와 달리 타입 표시가 필요없으니깐 그냥 넣으면됨
onClickFun= {
                tabNum--
                if(tabNum==0)
                    stageNum++
            }

-가장 처음엔 LemonadeApp()에 모든 로직과 ui 표시를 넣었는데 솔루션 코드를 보고 난 뒤 로직, ui를 분리하는게 좋아보여서 분리했다

 

package com.example.lemonade

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.lemonade.ui.theme.LemonadeTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            LemonadeTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {
                    LemonadeApp()
                }
            }
        }
    }
}

@Preview(showBackground = true)
@Composable
fun LemonadeApp(){
    var stageNum by remember{ mutableStateOf(0) }
    var messageId= stringResource(id = R.string.tap_lemon)
    var imageId= painterResource(id = R.drawable.lemon_tree)
    var descriptionId= stringResource(id = R.string.lemon_tree)
    var tabNum by remember { mutableStateOf(-1) }
    var onClickFun: ()->Unit={
        stageNum++
        tabNum=(2..4).random()
    }

    when(stageNum){
        1-> {
            messageId=stringResource(id = R.string.keep_tapping)
            imageId= painterResource(id = R.drawable.lemon_squeeze)
            descriptionId= stringResource(id = R.string.lemon)
            onClickFun= {
                tabNum--
                if(tabNum==0)
                    stageNum++
            }
        }
        2-> {
            messageId=stringResource(id = R.string.drink)
            imageId= painterResource(id = R.drawable.lemon_drink)
            descriptionId= stringResource(id = R.string.glass_of_lemonade)
        }
        3-> {
            messageId=stringResource(id = R.string.start_again)
            imageId= painterResource(id = R.drawable.lemon_restart)
            descriptionId= stringResource(id = R.string.empty_glass)
        }
    }
    TextAndImage(
        messageId=messageId,
        imageId=imageId,
        descriptionId=descriptionId,
        tabNum=tabNum,
        onClickFun=onClickFun
    )

}

@Composable
fun TextAndImage(
    messageId:String,
    imageId: Painter,
    descriptionId:String,
    tabNum:Int,
    onClickFun: ()->Unit
){
    Column(
        modifier = Modifier.fillMaxWidth(),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text(
            text = messageId,
            fontSize = 18.sp
        )
        Spacer(modifier = Modifier.height(16.dp))
        IconButton(
            onClick = onClickFun,
            modifier = Modifier.border(
                BorderStroke(2.dp, Color(105,205,216)),
                shape= RoundedCornerShape(4.dp)
            )
        ) {
            Image(
                painter = imageId,
                contentDescription = descriptionId,
            )
        }
    }
}

두번째는 이렇게 변수를 밖에 빼고 when에서 각 변수값 수정한 다음 맨 마지막에 TextAndImage라는 ui표시부분을 호출하도록 했는데 이렇게 하면 LemonadeApp()이 리컴포지션되면 항상 이 id를 담는 변수들이 초기화 되고 그다음 when에서 stageNum에 따라 변경된다, 가독성이 이게 더 좋을것 같아서 이렇게 해봤는데 오히려 줄 수만 많아지고 같은일 두번하게 만드는것 같아서 또 고쳤다

package com.example.lemonade

import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.example.lemonade.ui.theme.LemonadeTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            LemonadeTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colors.background
                ) {
                    LemonadeApp()
                }
            }
        }
    }
}

@Preview(showBackground = true)
@Composable
fun LemonadeApp(){//전체를 감싸며 로직을 처리한다
    var stageNum by remember{ mutableStateOf(0) }
    var tabNum by remember { mutableStateOf(-1) }

    when(stageNum){//stageNum에 따라 표시되는 ui가 달라진다
        0->{//레몬나무에서 레몬 선택하는 화면
            TextAndImage(
                messageId=stringResource(id = R.string.tap_lemon),
                imageId= painterResource(id = R.drawable.lemon_tree),
                descriptionId= stringResource(id = R.string.lemon_tree),
            ) {//전달할 onClick 처리 함수
                stageNum++
                tabNum=(2..4).random()
            }
        }
        1-> {//레몬즙을 짜는 화면
            TextAndImage(
                messageId=stringResource(id = R.string.keep_tapping),
                imageId= painterResource(id = R.drawable.lemon_squeeze),
                descriptionId= stringResource(id = R.string.lemon),
            ) {
                tabNum--
                if(tabNum==0)
                    stageNum++
            }
        }
        2-> {//레몬에이드를 마시는 화면
            TextAndImage(
                messageId=stringResource(id = R.string.drink),
                imageId= painterResource(id = R.drawable.lemon_drink),
                descriptionId= stringResource(id = R.string.glass_of_lemonade),
            ) {
                stageNum++
            }
        }
        3-> {//빈컵 화면
            TextAndImage(
                messageId=stringResource(id = R.string.start_again),
                imageId= painterResource(id = R.drawable.lemon_restart),
                descriptionId= stringResource(id = R.string.empty_glass),
            ) {
                stageNum=0
            }
        }
    }
}

@Composable
fun TextAndImage(//ui표기를 담당한다
    messageId:String,
    imageId: Painter,
    descriptionId:String,
    onClickFun: ()->Unit //()->Unit 타입의 함수를 매개변수로 받는다
){
    Column(
        modifier = Modifier.fillMaxWidth(),//가로를 차지가능한 만큼 다 차지한다
        horizontalAlignment = Alignment.CenterHorizontally,//가로중앙
        verticalArrangement = Arrangement.Center//세로중앙
    ) {
        Text(
            text = messageId,
            fontSize = 18.sp
        )
        Spacer(modifier = Modifier.height(16.dp))
        IconButton(
            onClick = onClickFun,
            modifier = Modifier.border(
                BorderStroke(2.dp, Color(105,205,216)),
                shape= RoundedCornerShape(4.dp)
            )
        ) {
            Image(
                painter = imageId,
                contentDescription = descriptionId,
            )
        }
    }
}

궁금한점...

LemonadeApp()에서 TextAndImage()를 호출할때 매개변수로 tabNum, stageNum을 전달했다

그런데 이게 전달할때 ... 주소로 전달되어서 다른 호출된 함수에서 값 변경했을때 원본변수값도 변경이 된건가?

실험을 하려고 그냥 일반 변수인 test를 만들어서 클릭할때마다 test++되게 하고 test도 TextAndImage로 전달했는데 걔는 변경이 안되는거 같음

그리고 여기서 ui, 로직처리 분리한게 https://origogi.github.io/android/compose-state/ 여기서 설명하는

State Hoisting 일까?

 

-참고사이트

 

https://github.com/google-developer-training/basic-android-kotlin-compose-training-lemonade/blob/main/app/src/main/java/com/example/lemonade/MainActivity.kt

 

GitHub - google-developer-training/basic-android-kotlin-compose-training-lemonade

Contribute to google-developer-training/basic-android-kotlin-compose-training-lemonade development by creating an account on GitHub.

github.com

https://origogi.github.io/android/compose-state/

 

[Android][Compose] State 관리

 

origogi.github.io

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31
글 보관함