#1 ์ ๋๋ฉ์ด์
LazyColumn์ ์์ดํ ์ด ์ถ๊ฐใ์ ๊ฑฐ๋ ๋์ ์ ๋๋ฉ์ด์ ํจ๊ณผ๋ฅผ ์ถ๊ฐํ๋ ค๊ณ ํ๋ค. ์ด ๋, ๊ทธ ๊ตฌํ ๋ฐฉ์์ (๋น์ฅ ๋ด๊ฐ ๋ณด๊ธฐ์) ์๋์ ๊ฐ์ด 2๊ฐ๋ก ๋๋ ์ ์๋ค. 2๊ฐ์ ๊ณต์ ๋ฌธ์ ๋งํฌ๋ฅผ ๋ฌ๊ฒ ๋ค. ๊ฐ๊ฐ์ ์๋ก ๋ค๋ฅธ ๊ตฌํ ๋ฐฉ์์ ์๋ ค์ฃผ๊ณ ์๋ค.
#1-1 'Composable'์ ์ ๋๋ฉ์ด์ ์ฒ๋ฆฌ๋ฅผ ์ํ ๊ฐ์ด๋
์ ๋๋ฉ์ด์ ์์ ์ ๋ฐ ์ปดํฌ์ ๋ธ | Jetpack Compose | Android Developers
์ด ํ์ด์ง๋ Cloud Translation API๋ฅผ ํตํด ๋ฒ์ญ๋์์ต๋๋ค. ์ ๋๋ฉ์ด์ ์์ ์ ๋ฐ ์ปดํฌ์ ๋ธ ์ปฌ๋ ์ ์ ์ฌ์ฉํด ์ ๋ฆฌํ๊ธฐ ๋ด ํ๊ฒฝ์ค์ ์ ๊ธฐ์ค์ผ๋ก ์ฝํ ์ธ ๋ฅผ ์ ์ฅํ๊ณ ๋ถ๋ฅํ์ธ์. Compose์๋ ์ผ๋ฐ์ ์ธ
developer.android.com
๊ฐ์ด๋์ ๊ธฐ์ ๋ ์ฝ๋์ ๊ตฌ์กฐ๋ ์๋์ ๊ฐ๋ค.
var visible by remember {
mutableStateOf(true)
}
// Animated visibility will eventually remove the item from the composition once the animation has finished.
AnimatedVisibility(visible) {
// your composable here
// ...
}
์ฃผ์ "your composable here" ๋ถ๋ถ์ ์ปดํฌ์ ๋ธ ํจ์๋ฅผ ๋ฃ์ผ๋ฉด, ํด๋น ์ปดํฌ์ ๋ธ ํจ์๊ฐ ํ๋ฉด์ ๋ณด์ฌ์ง ๋(= visible ํ๋กํผํฐ์ ๊ฐ์ดtrue๋ก ๋ณ๊ฒฝ๋ ๋) ์ ๋๋ฉ์ด์ ํจ๊ณผ๊ฐ ์ฒจ๊ฐ๋๋ค. AnimatedVisibility()์ ๋น์ทํ ๋์ ๋ฐฉ์์ ์ง๋ AnimatedContent() ๋ฐ Crossfade()๋ ์๋ค.
#1-2 'LazyColumn ๋ด item'์ ์ ๋๋ฉ์ด์ ์ฒ๋ฆฌ๋ฅผ ์ํ ๊ฐ์ด๋
๋ชฉ๋ก ๋ฐ ๊ทธ๋ฆฌ๋ | Jetpack Compose | Android Developers
์ด ํ์ด์ง๋ Cloud Translation API๋ฅผ ํตํด ๋ฒ์ญ๋์์ต๋๋ค. ๋ชฉ๋ก ๋ฐ ๊ทธ๋ฆฌ๋ ์ปฌ๋ ์ ์ ์ฌ์ฉํด ์ ๋ฆฌํ๊ธฐ ๋ด ํ๊ฒฝ์ค์ ์ ๊ธฐ์ค์ผ๋ก ์ฝํ ์ธ ๋ฅผ ์ ์ฅํ๊ณ ๋ถ๋ฅํ์ธ์. ๋ง์ ์ฑ์์ ํญ๋ชฉ์ ์ปฌ๋ ์ ์ ํ์ํด
developer.android.com
๊ฐ์ด๋์ ๊ธฐ์ ๋ ์ฝ๋์ ๊ตฌ์กฐ๋ ์๋์ ๊ฐ๋ค.
LazyColumn {
// It is important to provide a key to each item to ensure animateItem() works as expected.
items(books, key = { it.id }) {
Row(Modifier.animateItem()) {
// ...
}
}
}
Modifier์ animateItem()์ ๋ฉ์๋ ์ฒด์ด๋ํ๋ค. #1-1๋ #1-2๋ Jetpack Compose๋ต๊ฒ ๊ฐํธํด์ ์ข๋ค.
#1-3 ์ฒ๋ฆฌ ๋ฐฉ์ ๊ฒฐ์
#1-1์ ์ฝ๋๋ฅผ #1-2์ ๊ฒฝ์ฐ์๋ ์ ์ฉํ ์ ์๊ฒ ์ง๋ง, #1-2์์ ๋ณ๋์ ๋ฐฉ์์ ๊ธฐ์ ํ๋ค๋ ์ ์์ ์ด๋ค ์ด์ ๊ฐ ์์ ๊ฒ์ผ๋ก ์ถ์ธกํ๋ค. ๋จ์ง ๊ทธ ์ด์ ๋ง์ผ๋ก #1-2๋ฅผ ์ ํํ ์ด์ ๋ ์ถฉ๋ถํ๊ธด ํ๋ค. ๊ณต์์ด ์ํค๋ ๋๋ก ํ๋ ๊ฒ ์ฌ๋งํ๋ฉด ์ณ๋ค. ๋ฌผ๋ก ๊ทธ ์ ํ์ ์ด์ ๋ฅผ ์๋ฌด๊ฑฐ๋ ํ๋ ๊ณฑ์น์ด๋ณผ ์๋ ์๊ฒ ๋ค. ๋์ ๋ณด์ด๋ ์ด์ ๋ ์๊ณ . ๊ทธ๊ฑด ๋ฐ๋ก ํ์์ ๊ฐ์ด๋๋ item ๋ชฉ๋ก์ ์ผ๊ด์ ์ธ ์ ๋๋ฉ์ด์ ์ฒ๋ฆฌ๋ฅผ ํ ์ ์๋ค๋ ๊ฒ์ด๋ค. ๋ ๋ฉ์๋ ์ฒด์ด๋ ๋ฐฉ์์ด๋ผ๋ ์ ์์, ์ฌ์ฌ์ฉํ๊ฑฐ๋ ์ ์ฐํ๊ฒ ์ ์ฉํ๊ธฐ ํธํ ๊ฒ์ด๋ค.
#2 ์ฝ๋ - items()๋ก ์ ํ
#2-1 key
๋ฐ๋ก animateItem()์ ์ฌ์ฉํ๋ฉด ์ข๊ฒ ์ง๋ง, ๋ฌธ์์ "You should also provide a key via LazyListScope.item/LazyListScope.items for this modifier to enable animations. (์ ๋๋ฉ์ด์ ์ ํ์ฑํํ๋ ค๋ฉด, ์ด Modifier์๊ฒ LazyListScope.item/LazyListScope.items์ ํตํด key๋ฅผ ์ ๊ณตํด์ผ ํฉ๋๋ค.)"๋ผ๋ ๋ฌธ๊ตฌ๊ฐ ๋ณด์ธ๋ค. ์ฌ๊ธฐ์ ๋งํ๋ key๋, ๊ตฌ์ฒด์ ์ผ๋ก item() ๋๋ items()์ key ํ๋กํผํฐ๋ฅผ ์๋ฏธํ๋ค. item() ๋๋ items()์ ์์์ key๋ฅผ ํ ๋นํด๋์ผ๋ฉด ๋๋จธ์ง๋ ์์์ ์ผ๋ก ์ํ๋๋ค.
์ค์ ๋ก, key๊ฐ์ ์ ๊ณตํ์ง์์ ์ํ๋ก animateItem()๋ ์ฌ์ฉํด๋ดค๋๋ฐ ์ญ์ ์ ๋๋ฉ์ด์ ์ด ์ ์ฉ๋์ง ์์๋ค. ์ ๋๋ฉ์ด์ ์ ์ด๋ค ์์ดํ ์ ์ ์ฉํด์ฃผ์ด์ผ ํ๋๊ฐ?๋ฅผ ํ๋ก๊ทธ๋๋จธ๊ฐ Compose Runtime์๊ฒ ์๋ ค์ค์ผํ๋๋ฐ, ๊ทธ๋ฌ์ง ๋ชปํ์ผ๋.
์๋ ์ฝ๋๋ key์ ๋ฐ์ดํฐํ์ ๋ณผ ์ ์๋ LazyListScope.items() ํจ์์ ๋ชจ์์ด๋ค.
open fun items(
count: Int,
key: ((index: Int) -> Any)? = null,
contentType: (index: Int) -> Any? = { null },
itemContent: @Composable LazyItemScope.(index: Int) -> Unit
): Unit
#2-2 ๋ฌด์์ key๋ก ์จ์ผ ํ๋๊ฐ?
๋ด ํ๋ก์ ํธ์์ ๊ตฌํํ ์ ๋๋ฉ์ด์ ์, ๋ฑ๊ฐ์ ์์ดํ ์ ๊ฐ๊ฐ ๋ ๋ฆฝ์ ์ผ๋ก ์ ์ฉ๋์ด์ผ ํ๋ค. ๋ฐ๋ผ์ key๋ ๊ณ ์ ํด์ผ ํ๋ค. ๋, ์์ดํ ์ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ ์ฝ๋ ํ๋์ 1:1 ๋์๋๋ฏ๋ก ๋ ์ฝ๋์ id๋ฅผ ๊ทธ๋๋ก key๋ก ์ฌ์ฉํ๋ฉด ๋ ๊ฒ์ด๋ค.
#2-3 key ํ๋กํผํฐ ๋ถ์ฌ
// in NutrientScreen.kt
...
@Composable
fun NutrientScreen(
...
) {
...
LazyColumn(
...
) {
item {
...
}
...
itemsIndexed(dayMeals, key = { _, dayMeal -> dayMeal.mealId }) { index, dayMeal ->
Card(
...
) {
...
}
}
}
}
๊ธฐ์กด์ ์ฌ์ฉํ๋ฉด LazyListScope.itemsIndexed()๋ ์ด์ ํ์์๋ค. ๋ ์ฝ๋๋ฅผ ์๋ณํ ์ ์๋ key๊ฐ์ด ์๊ฒผ์ผ๋ ์ญํ ์ด ์ค๋ณต๋๋ ๊ฒ์ด๋ค. itemIndexed()๋ฅผ items()๋ก ๋ฐ๊พผ๋ค.
#2-4 items()๋ก ์ ํ
// in NutrientScreen.kt
...
@Composable
fun NutrientScreen(
...
) {
...
LazyColumn(
...
) {
item {
...
}
...
items(dayMeals, key = { it.mealId }) { dayMeal ->
val key = remember { dayMeal.mealId }
Card(
...
) {
...
}
}
}
}
items()๋ก ์ ํํ ์ฝ๋๋ค. key = ... ๋ถ๋ถ์ ๋๋ค ํํ์ ๋ฌธ๋ฒ์ด ๊ฐ์ํ๋๋ค. itemsIndexed()์ key ํ๋กํผํฐ ํ์์ ((index: Int, item) -> Any)?์ด๊ณ , items()๋ ((index: Int) -> Any)?๋ค. ๋งค๊ฐ๋ณ์๊ฐ 2๊ฐ์์ 1๊ฐ๋ก ์ค์์ผ๋ฏ๋ก, it ํค์๋๋ฅผ ์ฌ์ฉํ์ผ๋ฉฐ ๊ทธ๋์ ์๊ฐ์ ์ผ๋ก ๊ฐ๋ฒผ์์ก๋ค.
๋, (๋ด ํ๋ก์ ํธ์ ๊ฒฝ์ฐ) items() content ๋ถ๋ถ ๋ก์ง ์ฒ๋ฆฌ๋ฅผ ์ํด key๋ฅผ ๋ช ์์ ์ผ๋ก ์ฌ์ฉํ ํ์๊ฐ ์๊ธฐ์ ๋ณ๋์ ๋ก์ปฌ ํ๋กํผํฐ๋ ์ ์ธํด์ฃผ์๋ค.
#3 ์ฝ๋ - ์ ๋๋ฉ์ด์ ์ ์ฉ
#3-1 animateItem()
// in NutrientScreen.kt
@Composable
fun NutrientScreen(
...
) {
...
LazyColumn(
...
) {
item {
...
}
...
items(dayMeals, key = { it.mealId }) { dayMeal ->
val key = remember { dayMeal.mealId }
Card(
modifier = Modifier
...
.padding(
...
top = if (key == dayMeals.last().mealId) 8.dp else 0.dp,
...
)
.animateItem(),
...
) {
Box(
...
) {
Column(
...
) {
Text(
...
)
Text(
text = "mealId: ${dayMeal.mealId}",
...
)
}
IconButton(
...
) {
...
}
}
}
}
}
}
animateItem()์ Card์ Modifier์ ๋ฉ์๋ ์ฒด์ด๋ํ๋ค. ๋, index๊ฐ ์ฌ๋ผ์ก์ผ๋ฏ๋ก index์ ์์กดํ๋ ๋ค๋ฅธ ์ฝ๋๋ค๋ ์ ์ ํ๊ฒ ๋ฐ๊ฟ์ค๋ค.
#3-2 animateItem() ์ปค์คํ
open fun Modifier.animateItem(
fadeInSpec: FiniteAnimationSpec<Float>? = spring(stiffness = Spring.StiffnessMediumLow),
placementSpec: FiniteAnimationSpec<IntOffset>? = spring(
stiffness = Spring.StiffnessMediumLow,
visibilityThreshold = IntOffset.VisibilityThreshold
),
fadeOutSpec: FiniteAnimationSpec<Float>? = spring(stiffness = Spring.StiffnessMediumLow)
): Modifier
์ ์ฝ๋๊ฐ ์๋ ๊ณต์ ๋ฌธ์์ ๋งํฌ. #3-1์์๋ animateItem() 3๊ฐ์ง ํ๋กํผํฐ์ ๊ธฐ๋ณธ๊ฐ์ธ spring(stiffness = Spring.StiffnessMediumLow), spring(stiffness = Spring.StiffnessMediumLow, visibilityThreshold = IntOffset.VisibilityThreshold), spring(stiffness = Spring.StiffnessMediumLow)์ด ๊ฐ๊ฐ ์ ์ฉ๋์๋ค.
์ธ ํ๋กํผํฐ ์ ๋ถ FiniteAnimationSpec ๋ฐ์ดํฐํ์ธ๋ฐ, ์ฌ๊ธฐ์ ํ ๋นํ ๋งํ ํด๋์ค๋ SpringSpec์ TweenSpec์ด ์๋ค. SpringSpec์์ Spring์ ์ฐ๋ฆฌ๊ฐ ์๋ ๊ทธ ์คํ๋ง(์ฉ์์ฒ )์ ์๋ฏธํ๋ค. ์ฉ์์ฒ ์ด ํ๊ธฐ๋ ์ ๋๊ฐ(dampingRatio)๊ณผ ์ฉ์์ฒ ์ ๊ฐ๋(stiffness)๋ก ์ ๋๋ฉ์ด์ ์ ์กฐ์ ํ๋ค. TweenSpec์ ์ข SpringSpec์ ๋นํด ์ข ๋ ์ธ์ธํ๋ค(๋ช ๋ น์ ์ด๋ค). TweenSpec์ ์๊ฐ์ ๊ธฐ๋ฐํ๋ค. ์ ๋๋ฉ์ด์ ์ด ์๋ฃ๋๊ธฐ๊น์ง์ ์๊ฐ(durationMillis), ์ ๋๋ฉ์ด์ ์์ ์ ๋๊ธฐ ์๊ฐ(delayMillis), ์ ๋๋ฉ์ด์ ์ ์คํ์ผ(์๋ ๊ณก์ ) (easing)๋ก ์ ๋๋ฉ์ด์ ์ ์กฐ์ ํ๋ค.
ํ์ฌ๋ ์ฐ์ ๊ธฐ๋ณธ๊ฐ์ ์ฐ๊ณ ๋์ด๊ฐ์ง๋ง, ๋์ค์๋ผ๋ ์ ๋๋ฉ์ด์ ์ ์ฑ์ ์ปจ์ ์ ๋ง๊ฒ ์์ ํ ์ผ์ด ํ์ํ ๊ฒ ๊ฐ๋ค. ๊ทธ๋์ ์ด๋ ๊ฒ ๊ฐ๋จํ ์ ๋๋ฉ์ด์ ์ ์ปค์คํ ์ ๋ํด ๊ธฐ๋กํด๋๋ค.
#4 ์์ฑ๋ ์ฑ
#4-1 ์๋ ์์
#4-2 ์ด ๊ฒ์๊ธ ์์ ์ Commit
GitHub - Kanmanemone/nutri-capture-new
Contribute to Kanmanemone/nutri-capture-new development by creating an account on GitHub.
github.com
#4-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 ํ๋ก ํธ์๋ - bottomBar ๋์ ๋ณ๊ฒฝ (1) | 2024.12.18 |
---|---|
Nutri Capture ๋ฐฑ์๋ - ์ด์ง ํ์ ์ ์ฉ (1) | 2024.12.17 |
Nutri Capture ํ๋ก ํธ์๋ - INSERT ๋ฐ DELETE ๋ฒํผ ๊ตฌํ (1) | 2024.11.28 |
Nutri Capture ๋ฐฑ์๋ - DeleteDayMeal ์ด๋ฒคํธ ์ถ๊ฐ (0) | 2024.11.28 |
Nutri Capture ๋ฐฑ์๋ - DAO ๋ ผ๋ฆฌ์ ์ค๋ฅ ์์ (0) | 2024.11.26 |