본문 바로가기
Android/Kotlin

Android 배워보자 Compose! -(4)

by wannagohome97 2024. 1. 12.

이전 글

 

 

Android 배워보자 Compose! -(3)

이전 글 Android 배워보자 Compose! (2) 이전 글.. https://developanything.tistory.com/entry/Android-Compose-%EB%B0%B0%EC%9B%8C%EB%B3%B4%EA%B8%B0-1 Android 배워보자 Compose - (1) Compose 가 뭐지? https://developer.android.com/jetpack/compose/

developanything.tistory.com

 

Compose 환경에서 ViewModel 과 LiveData 사용하기

 

프로젝트에서 ViewModel 과 LiveData 를 이용한 MVVM 구조를 많이들 사용하셨을텐데

Compose 환경에서도 위 두 개념을 사용할 수 있습니다.(하지만 State 나 Flow 를 조금 더 지향한다고 알고있습니다.)

아래는 구글의 공식 예시 소스코드와 학습 튜토리얼입니다.(저걸 보러 가시는 것도 나쁘진 않습니다.)

 

 

Compose의 ViewModel 및 상태  |  Android Developers

이 Codelab에서는 아키텍처 구성요소 중 하나인 ViewModel을 사용하는 방법을 알아봅니다. 구성 변경 중에 앱 상태를 유지하도록 ViewModel을 구현합니다.

developer.android.com

 

 

시작 전에 Module Level 의 build.gradle 에 아래와 같이 의존성을 추가해줍니다

implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.7.0")
implementation("androidx.compose.runtime:runtime-livedata:1.5.4")

 

 

 

ViewModel

기본적으로 ViewModel 을 사용하는 과정은 아래와 같은데

  • ViewModel class 구현
  • ViewModelProvider.Factory 를 상속받는 Factory class 구현
  • View 에 ViewModel 을 매핑

ViewModel 은 Singleton 으로 만들어줘야 하기 때문에 2번째의 ViewModelFactory 도 같이 구현해줘야합니다.

 

여기서 Singleton Pattern 이란 객체의 인스턴스가 단 1개(Single) 존재하는 소프트웨어 디자인 패턴입니다.

 

아래는 ViewModel 클래스입니다.

class StringViewModel : ViewModel(){
    
    private val mStringViewModel = MutableLiveData<String>()
    
    val data : LiveData<String>
        get() {
            return mStringViewModel
        }
    
    init {
        mStringViewModel.value = ""
    }
    
    fun setData(string: String){
        mStringViewModel.value = string
    }
    
}

 

Factory 클래스는 아래와 같이 구현할 수 있습니다.

class StringViewModelFactory(): ViewModelProvider.Factory{

    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(StringViewModel::class.java)) {
            return StringViewModel() as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
    
}

 

 

여기까진 일반적으로 ViewModel 패턴을 사용할 때와 크게 다르지 않습니다.

 

위에서 구현해둔 ViewModel 과  Factory 를 이용해 Composable 내부에서 이런 식으로 호출할 수 있습니다.

val stringViewModel : StringViewModel = viewModel(factory = StringViewModelFactory())

 

LiveData

Compose 에서는 LiveData를 observeAsState() 메서드를 이용하여 State 객체로 받아올 수 있고

이 State 객체의 getValue() 메서드를 이용하여 해당 LiveData 의 Value(값) 을 사용할 수 있습니다.

val liveData = stringViewModel.data.observeAsState()
val value: String? by liveData

 

 

위에서 구현해둔 데이터를 바탕으로 버튼을 누르면

ViewModel 에 data 를 전달하는 코드 예제를 아래와 같이 구현해보았습니다.

(누르면 랜덤 야식 추천을 해주는 예제입니다..ㅎㅎ)

val foodList: List<String> = listOf("치킨",
    "피자",
    "라면",
    "햄버거",
    "떡볶이",
    "족발",
    "만두",
    "치즈스틱")

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ComposableTheme {
                LunchMenu()
            }
        }
    }
}
@Preview
@Composable
fun LunchMenu(){
    val stringViewModel : StringViewModel = viewModel(factory = StringViewModelFactory())
    val liveData = stringViewModel.data.observeAsState()
    val value: String? by liveData
    Box(modifier = Modifier.fillMaxSize()){
        Text(
            modifier = Modifier.align(Alignment.Center),
            text = "오늘 야식 뭐먹지????\n\n" +
                    "$value",
            textAlign = TextAlign.Center)
        Button(onClick = {
            val lunchMenu = foodList.random()
            stringViewModel.setData(lunchMenu)
        }, modifier = Modifier.align(Alignment.BottomCenter)
            .offset(y=(-100).dp)
        ) {
            Text(text = "돌려!")
        }
    }
}

 

 

 

위 코드의 시연 영상으로 마무리하겠습니다.

하지만 먹지 않았습니다.