#1 ๊นจ์ ๋ณ๊ฒฝ ์ฌํญ
#1-1 "NutrientInputScreen"์ "NutrientScreen"์ผ๋ก ๋ณ๊ฒฝ
๊ฐ๋
์ฑ์ ์ํ ์ด๋ฆ ๋ณ๊ฒฝ์ด๋ค.
#1-2 "nutrient" ํจํค์ง ์์ฑ
๋จผ์ , "NutrientScreen"๋ฅผ "nutrient" ํจํค์ง์ ๋ฃ๋๋ค. ๋, ๋ณธ ๊ฒ์๊ธ์์ ์๋ก ๋ง๋ค ๋ชจ๋ ํ์ผ์ ํด๋น ํจํค์ง์ ๋ค์ด๊ฐ๋ค
#2 ๊ฐ์
#2-1 ๊ฐ์ฒด ์งํฅ์ UI ์ค๊ณ
[Android] Jetpack Compose - ๊ฐ์ฒด ์งํฅ์ UI ๋ ์ด์ด ์ค๊ณ
#1 ๊ฐ์ UI ๋ ์ด์ด | Android Developers์ด ํ์ด์ง๋ Cloud Translation API๋ฅผ ํตํด ๋ฒ์ญ๋์์ต๋๋ค. UI ๋ ์ด์ด ์ปฌ๋ ์ ์ ์ฌ์ฉํด ์ ๋ฆฌํ๊ธฐ ๋ด ํ๊ฒฝ์ค์ ์ ๊ธฐ์ค์ผ๋ก ์ฝํ ์ธ ๋ฅผ ์ ์ฅํ๊ณ ๋ถ๋ฅํ์ธ์. UI์ ์ญ
kenel.tistory.com
์ ๊ฒ์๊ธ์ ๊ธฐ๋ฐํ์ฌ ์ฝ๋๋ฅผ ์งฐ๋ค.
#2-2 ๋ง๋ค ํ์ผ
1. (View์ ์ญํ ) nutrient.NutrientScreen
2. (Event์ ์ญํ ) nutrient.NutrientScreenEvent
3. (State์ ์ญํ ) nutrient.NutrientScreenState
4. (ViewModel์ ์ญํ ) nutrient.NutrientViewModel
5. (Event์ ์ญํ ) nutrient.NutrientViewModelEvent
6. (๋ฐ์ดํฐ ํด๋์ค) nutrient.NutritionInfo
1. NutrientScreen
๊ธฐ์กด์ ์๋ ํ์ผ์ด๋ค. View์ ์ญํ ์ ์ํํ๋ค.
2. NutrientScreenEvent
ViewModel์์ View์๊ฒ ์์ฒญํ๋ ์ด๋ฒคํธ๋ค. ํด๋น ์ด๋ฒคํธ์ ์ธ์คํด์ค๋ ViewModel์ Flow๋ก ์ ์ํ๋ฉฐ, ํด๋น Flow๋ฅผ View์์ collect()ํ์ฌ ์ด๋ฒคํธ๋ฅผ ์ฒ๋ฆฌํ๋ค (์ฐธ์กฐ: ์ด ๊ฒ์๊ธ์ #4).
3. NutrientScreenState
View๊ฐ ๋ณด์ ํ ์ ๋ณด๋ฅผ ์ ์ํ๋ค. ์ธ์คํด์ค๋ ViewModel์ ์ ์ธ๋๋ค.
4. NutrientViewModel
View๋ฅผ ๋ณด์กฐํ๋ ViewModel์ด๋ค.
5. NutrientViewModelEvent
View์์ ViewModel์๊ฒ ์์ฒญํ๋ ์ด๋ฒคํธ๋ค. ์ธ์คํด์ค๋ ๊ทธ๋ ๊ทธ๋ ๋ง๋ค์ด ์ด๋ค. ์๋ํ๋ฉด NutrientScreenEvent์ฒ๋ผ collect()๋ก ์ฒ๋ฆฌํ๋ ๋ฐฉ์์ด ์๋๊ธฐ ๋๋ฌธ์ด๋ค. View์์ ViewModel์ ์ด๋ฒคํธ ์ฒ๋ฆฌ ํจ์๋ฅผ ์ง์ ํธ์ถํ๋ฉฐ NutrientViewModelEvent๋ฅผ ์ ๋ฌํ ๊ฒ์ด๋ค.
6. NutritionInfo
์ด ๊ฒ์๊ธ์ #2-4์์ ๋งํ๋ '๊ฐ์ธํ๋ ์ฒดํฌ๋ฆฌ์คํธ'๋ฅผ ์ ์ฅํ ๊ณณ์ด๋ค. ์ฌ๊ธฐ์ ์ธ๋ถ ์ฌํญ์ ๊ตฌํํ์ง๋ ์๋๋ค (ํ๋ง ์ก์).
#3 ์ฝ๋ - NutrientViewModel
#3-1 NutrientViewModel.kt ์์ฑ
// package com.example.nutri_capture_new.nutrient
import androidx.lifecycle.ViewModel
class NutrientViewModel : ViewModel() {
}
#3-2 Jetpack Compose์ฉ ViewModel ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ค์ด๋ก๋ - ๋ชจ๋ ์์ค build.gradle
plugins {
...
}
android {
...
}
dependencies {
...
// ViewModel (Compose)
implementation(libs.androidx.lifecycle.viewmodel.compose)
}
์ด์ #2-1์์ ๋ง๋ ViewModel์ ์ฌ์ฉํด์ผํ ํ
๋ฐ, ๋ณธ ๊ฒ์๊ธ์ฒ๋ผ Jetpack Compose ํ๋ก์ ํธ์ ๊ฒฝ์ฐ๋ผ๋ฉด ViewModel์ ์ฌ์ฉํ๊ธฐ ์ํด์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ํ๋๊ฐ ์๊ตฌ๋๋ค. ํน์ ํ๋ก์ ํธ์ libs.versions ํ์ผ์ด ์๋ค๋ฉด implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.6")๋ผ๊ณ ์
๋ ฅํ๋ฉด ๋๋ค.
#3-3 Jetpack Compose์ฉ ViewModel ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๋ค์ด๋ก๋ - libs.versions
[versions]
...
[libraries]
...
androidx-lifecycle-viewmodel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "lifecycleRuntimeKtx" }
...
[plugins]
...
#3-4 View์ ViewModel ์ฃผ์ (์์ฑ์ ์ฃผ์ )
// package com.example.nutri_capture_new.nutrient
...
import androidx.lifecycle.viewmodel.compose.viewModel
@Composable
fun NutrientScreen(
scope: CoroutineScope,
snackbarHostState: SnackbarHostState,
viewModel: NutrientViewModel = viewModel<NutrientViewModel>()
) {
...
}
NutrientScreen์์ ์์ฑ์ ์ธ์์ ViewModel๋ฅผ ์ฃผ์
ํ๋ค.
#4 ์ฝ๋ - NutrientScreenState
#4-1 NutrientScreenState.kt ์์ฑ
// package com.example.nutri_capture_new.nutrient
import java.time.LocalDate
import java.time.LocalTime
data class NutrientScreenState(
val dailyMeals: List<DailyMeal>
)
data class DailyMeal(
val date: LocalDate,
val meals: List<Meal>
)
data class Meal(
val time: LocalTime,
val name: String,
val nutritionInfo: NutritionInfo,
)
val dailyMeals: List<DailyMeal>
View์ LazyColumn์์ ํ์ํ ์์ดํ
๋ค์ ๋ฆฌ์คํธ๋ค.
data class DailyMeal
ํ๋ฃจ์ ๋จน์ ์์ฌ๋ ๊ฐ์๋ค์ ๋ณด์ ํ๋ ํด๋์ค๋ค.
data class Meal
์์ฌ๋ ๊ฐ์์ ๋จน์ ์๊ฐ, ์ด๋ฆ, ์์ ์ ๋ณด๋ฅผ ๋ณด์ ํ๋ค. NutritionInfo ํด๋์ค๋ ์๋์ ์๋ค.
#4-2 NutritionInfo.kt ์์ฑ
// package com.example.nutri_capture_new.nutrient
data class NutritionInfo(
val mealSize: Int
)
์ธ๋ถ ๊ตฌํ์ ๋์ค์ ํ๋ค. ์ฐ์ ์ ์ผ๋ง๋ ๋จน์๋์ง(๊ณผ์ ์ ๋)๋ฅผ ํํํ๋ ๋ณ์ ํ๋๋ง ๋ฉ๊ทธ๋ฌ๋ ๋ฃ์ด๋์๋ค.
#4-3 ViewModel์ NutrientScreenState๋ฅผ ๋ด๋ State ๋ณ์ ์ ์ธ
...
class NutrientViewModel : ViewModel() {
// (1) ํ๋ฉด ํ์์ฉ State
private val _nutrientScreenState = mutableStateOf(
NutrientScreenState(
dailyMeals = emptyList()
)
)
val nutrientScreenState: State<NutrientScreenState>
get() = _nutrientScreenState
}
NutrientScreenState๋ ์ด๋ฆ์ "State"๊ฐ ๋ค์ด๊ฐ์ง๋ง ์ง์ง State๋ ์๋๋ค. NutrientScreenState ํด๋์ค์ ์ธ์คํด์ค๋ฅผ mutableStateOf( ... )์ ๋ฃ์์ผ๋ก์จ ์ง์ง State๋ก ๋ง๋ ๋ค.
#5 ์ฝ๋ - NutrientScreenEvent
#5-1 NutrientScreenEvent.kt ์์ฑ
// package com.example.nutri_capture_new.nutrient
sealed class NutrientScreenEvent {
data class ShowSnackbar(val message: String) : NutrientScreenEvent()
}
์ค๋ต๋ฐ๋ฅผ ํ์ํ๋ ์ด๋ฒคํธ ํ๋๋ฅผ ์์ ์ด๋ฒคํธ๋ก ์ ์ํ๋ค. ๋ค๋ฅธ ์ด๋ฒคํธ๊ฐ ํ์ํ๋ค๋ฉด ๋์ค์ ์ถ๊ฐํ๊ฒ ๋ค.
#5-2 ViewModel์์ ์ด๋ฒคํธ๋ฅผ ์ ์ฅํ๋ Flow ์ธ์คํด์ค ์์ฑ
...
class NutrientViewModel : ViewModel() {
// (1) ํ๋ฉด ํ์์ฉ State
...
// (2) View์์ ๋ฐ์ ์ฒ๋ฆฌํ ์ด๋ฒคํธ
private val _nutrientScreenEventFlow = MutableSharedFlow<NutrientScreenEvent>()
val nutrientScreenEventFlow: SharedFlow<NutrientScreenEvent>
get() = _nutrientScreenEventFlow.asSharedFlow()
}
ViewModel์ ์ด๋ฒคํธ๋ฅผ ๋ด๋ SharedFlow๋ฅผ ์ ์ธํ๋ค.
#5-3 View์์ ์ด๋ฒคํธ๋ฅผ ์ ์ฅํ๋ Flow ์ธ์คํด์ค collect()
...
@Composable
fun NutrientScreen(
scope: CoroutineScope,
snackbarHostState: SnackbarHostState,
viewModel: NutrientViewModel = viewModel<NutrientViewModel>()
) {
LaunchedEffect(key1 = true) {
// ViewModel๋ก๋ถํฐ ๋ฐ์ ์ด๋ฒคํธ ์ฒ๋ฆฌ
viewModel.nutrientScreenEventFlow.collectLatest { event ->
when (event) {
is NutrientScreenEvent.ShowSnackbar -> {
scope.launch {
snackbarHostState.showSnackbar(
message = event.message,
duration = SnackbarDuration.Short
)
}
}
}
}
}
}
ViewModel์ SharedFlow๋ฅผ collect()ํ๊ณ ํด๋น Flow ์์ ์์ ๋์์ ์ ์ํ๋ค.
#6 ์ฝ๋ - NutrientViewModelEvent
#6-1 NutrientViewModelEvent.kt ์์ฑ
// package com.example.nutri_capture_new.nutrient
sealed class NutrientViewModelEvent {
data object InitializeState : NutrientViewModelEvent()
}
State๋ฅผ ์ด๊ธฐํํ๋ ์ด๋ฒคํธ ํ๋๋ฅผ ์์ ์ด๋ฒคํธ๋ก ์ ์ํ๋ค. ๋ค๋ฅธ ์ด๋ฒคํธ๊ฐ ํ์ํ๋ค๋ฉด ๋์ค์ ์ถ๊ฐํ๋ค.
#6-2 ViewModel์์ ์ด๋ฒคํธ๋ฅผ ๋ฐ์ ์ฒ๋ฆฌํ๋ ํจ์ onEvent() ์ ์ธ
...
class NutrientViewModel : ViewModel() {
// (1) ํ๋ฉด ํ์์ฉ State
...
// (2) View์์ ๋ฐ์ ์ฒ๋ฆฌํ ์ด๋ฒคํธ
...
// (3) View๋ก๋ถํฐ ๋ฐ์ ์ด๋ฒคํธ ์ฒ๋ฆฌ
fun onEvent(event: NutrientViewModelEvent) {
when (event) {
is NutrientViewModelEvent.InitializeState -> {
_nutrientScreenState.value = _nutrientScreenState.value.copy(
dailyMeals = listOf(
DailyMeal(
date = LocalDate.of(2011, 11, 11),
meals = emptyList()
),
DailyMeal(
date = LocalDate.of(2011, 11, 12),
meals = emptyList()
),
DailyMeal(
date = LocalDate.of(2011, 11, 13),
meals = emptyList()
),
DailyMeal(
date = LocalDate.of(2011, 11, 14),
meals = emptyList()
),
DailyMeal(
date = LocalDate.of(2011, 11, 15),
meals = emptyList()
)
)
)
}
}
}
}
๋๋ฏธ์ฉ์ผ๋ก, 2011๋
11์ 11์ผ๋ถํฐ 15์ผ๊น์ง์ DailMeal ์ธ์คํด์ค 5๊ฐ๋ฅผ ์ด๊ธฐํํ๊ฒ๋ ๋๊ฒ ๋ค.
#6-3 View์์ State๋ฅผ ์ด๊ธฐํํ๋ ์ด๋ฒคํธ ViewModel์ ๋ณด๋ด๊ธฐ
...
@Composable
fun NutrientScreen(
scope: CoroutineScope,
snackbarHostState: SnackbarHostState,
viewModel: NutrientViewModel = viewModel<NutrientViewModel>()
) {
LaunchedEffect(key1 = true) {
// State ์ด๊ธฐํ
viewModel.onEvent(NutrientViewModelEvent.InitializeState)
// ViewModel๋ก๋ถํฐ ๋ฐ์ ์ด๋ฒคํธ ์ฒ๋ฆฌ
...
}
}
'State ์ด๊ธฐํ' ์ฝ๋๋ 'ViewModel๋ก๋ถํฐ ๋ฐ์ ์ด๋ฒคํธ ์ฒ๋ฆฌ' ๋ณด๋ค LaunchedEfffect { ... } ๋ด์์ ๋จผ์ ์คํ๋์ด์ผ ํ๋ค. ๊ทธ๋ ์ง ์์ผ๋ฉด 'State ์ด๊ธฐํ' ์ฝ๋๊ฐ ์คํ๋์ง ์๋๋ค. ์๋ง collectLastest()๊ฐ suspend ํค์๋๊ฐ ๋ถ๋ ๋น๋๊ธฐ ์ฝ๋๋ผ์ ๋ฐ์ํ๋ ๋ฌธ์ ๋ก ๋ณด์ธ๋ค.
#6-4 ๊ฐ๋จํ LazyColumn ์ ์ธ
...
@Composable
fun NutrientScreen(
scope: CoroutineScope,
snackbarHostState: SnackbarHostState,
viewModel: NutrientViewModel = viewModel<NutrientViewModel>()
) {
LaunchedEffect(key1 = true) {
...
}
LazyColumn(
modifier = Modifier
.fillMaxSize()
.background(Color.DarkGray)
) {
val dailyMeals = viewModel.nutrientScreenState.value.dailyMeals
items(dailyMeals) { dailyMeal ->
Text(
text = dailyMeal.date.toString(),
color = Color.White,
fontSize = 40.sp
)
}
}
}
์ง์ ํ์ ๋ฐฐ๊ฒฝ LazyColumn์ ํ์ ๊ธ์จ item์ด ๋์ค๋ ๊ฐ๋จํ ์ฝ๋๋ค. #5-2์์ ๋ง๋ค์ด๋์๋ ๋๋ฏธ DailyMeal ์ธ์คํด์ค 5๊ฐ๊ฐ ๋ณด์ฌ์ผ ํ ๊ฒ์ด๋ค. #7-1์ ํด๋น ํ๋ฉด ์คํฌ๋ฆฐ์ท์ด ์๋ค.
#7 ์์ฝ
NutrientScreen ๊ตฌํ์ ์ํ ํฐ ํ์ ๊ฐ์ฒด ์งํฅ์ ์ธ ๋ฐฉ์์ผ๋ก ์ก์๋์๋ค.
#8 ์์ฑ๋ ์ฑ
#8-1 ์คํฌ๋ฆฐ์ท

#8-2 ์ด ๊ฒ์๊ธ ์์ ์ Commit
GitHub - Kanmanemone/nutri-capture-new
Contribute to Kanmanemone/nutri-capture-new development by creating an account on GitHub.
github.com
#8-3 ๋ณธ ํ๋ก์ ํธ์ ๊ฐ์ฅ ์ต์ Commit
GitHub - Kanmanemone/nutri-capture-new
Contribute to Kanmanemone/nutri-capture-new development by creating an account on GitHub.
github.com
'๊ฐ๋ฐ ์ผ์ง ๐ป > Nutri Capture' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
Nutri Capture ํ๋ก ํธ์๋ - ๋ฌดํ ์คํฌ๋กค ๋ก์ง ๋ถ๋ฆฌ (0) | 2024.10.12 |
---|---|
Nutri Capture ํ๋ก ํธ์๋ - NutrientScreen์ ๋ฌดํ ์คํฌ๋กค (1) | 2024.10.11 |
Nutri Capture ๋ฐฉํฅ์ฑ - ๊ณ ์ ์ธ์ง๊ฐ๊ฐ, ์ UI ์ค์ผ์น (4) | 2024.10.03 |
Nutri Capture ํ๋ก ํธ์๋ - NavigationBar (1) | 2024.09.28 |
Nutri Capture ํ๋ก ํธ์๋ - NavHost (1) | 2024.09.26 |