#1 ์คํฌ๋กค ํจ์ ๋ณ๊ฒฝ
#1-1 ๊ธฐ์กด ํจ์
LazyListState.scrollToItem() ๋ฐ LazyListState.scrollBy()๋, ์์คํ
์์ ์ ์ฝ์ด ์กด์ฌํ๋ค. ์ฌ์ฉ์๊ฐ ํ๋ฉด์ ์์ ๋ถ์ธ ์ฑ๋ก ์ ์งํ๋ฉด ์คํฌ๋กค์ด ์์ ์ ๊ฒจ๋ฒ๋ฆฌ๊ธฐ ๋๋ฌธ์ด๋ค. ์ด ์์คํ
์์ ์ ์ฝ์ ์ฐํํ๋ ค๊ณ ์ ๋ง ๋ง์ ์ฝ๋๋ฅผ ์๋ํด๋ณด์์ง๋ง, ์ ๋๋ก ์๋ํ๊ธฐ ์์๊ณ ์๋ํ๋๋ผ๋ ์ฑ์ด ๊ต์ฅํ ์กฐ์กํด๋ณด์ด๋ ๋ชจ์์์๋ค.
#1-2 LazyListState.requestScrollToItem()
LazyListState | Android Developers
developer.android.com
๊ทธ๋ฌ๋ค ์ฐพ์ ํจ์๊ฐ LazyListState.requestScrollToItem()๋ค. ๋ค์ Recomposition ๋ ์คํฌ๋กค์ด ์์นํด์ผํ๋ ๋ถ๋ถ์ ์ง์ ํ๋ ํจ์๋ก, LazyListState.scrollToItem() ๋ฐ LazyListState.scrollBy()์ฒ๋ผ ๋ช
๋ น์ ์ผ๋ก ์คํฌ๋กค์ ์กฐ์ํ๋ ๊ฒ ์๋๋ผ ์ ์ธ์ ์ผ๋ก Compose Runtime์๊ฒ ์คํฌ๋กค ์์น๋ฅผ ๋ถํํ๋ ํจ์๋ค. '์คํฌ๋กค ์ง์ ์กฐ์ ๋ช
๋ น'์ ์ฌ์ฉ์์ ํฐ์น ์ด๋ฒคํธ์ ์ํด ์ฐ์ ์์๊ฐ ๋ฐ๋ ค Blocking๋ ์ ์์ง๋ง, Recomposition ์์ ๊ฒฐ์ ๋๋ ์คํฌ๋กค ์์น๋ ์ฌ์ฉ์์ ํฐ์น ์ด๋ฒคํธ๊ณผ ๋
๋ฆฝ์ ์ด๋ค. ๋ฐ๋ผ์ ๋ด๊ฐ ๊ตฌํํ๊ณ ์ ํ๋ ๋งค์ฐ ๊น๋ํ๊ณ ๋งค๋๋ฌ์ด ์ญ๋ฐฉํฅ ๋ฌดํ ์คํฌ๋กค์ด ๊ตฌํ๋๋ค. ๋, LazyListState.requestScrollToItem()๋ LazyListState.scrollToItem() ๋ฐ LazyListState.scrollBy()์ฒ๋ผ ๋น๋๊ธฐ ์ฝ๋๊ฐ ์๋๋ผ ๋๊ธฐ ์ฝ๋๋ค (์ฆ, suspend ํค์๋๊ฐ ๋ถ์ง ์๋๋ค). requestScrollToItem()์ ๋ค์ Recomposition ๋์ ์์
์ ์์ฒญ๋งํ๋ ํจ์์ด๊ธฐ ๋๋ฌธ์ด๋ค.
... ์ฒ์๋ถํฐ ๊ณต์ ๋ฌธ์๋ฅผ ๊ผผ๊ผผํ ์ฝ์ ๊ฑธ ๊ทธ๋ฌ๋ค. ์ด๋ฏธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์กด์ฌํ๋ ๊ธฐ๋ฅ์ ํผ์์ ๊ตฌํํด๋ณด๋ ค๊ณ ๋ฉฐ์น ๊ฐ ์ฉ์ฉ๋งจ ๊ผด์ด ์๋๊ฐ. ์๋ฌด๋๋ ์์ด๋ผ์ ๊ทธ๋ฐ ๊ฒ๋ ๊ฐ๋ค. ๊ทธ๋ผ์๋ ๋ฒ์ญ๊ธฐ๊น์ง ๋์ํด์ ์ฝ์ ๊ฐ์น๋ ์ถฉ๋ถํ๋ค๊ณ ์๊ฐํ๋ค. ๋ช ์๊ฐ์ ๊ณต์ ๋ฌธ์ ์ ๋
์ด ๋ช์ญ ์๊ฐ์ ์๊ปด์ค๋ค.
#2 ์ฝ๋
#2-1 NutrientScreenEvent.ScrollToItem ์ด๋ฒคํธ ์ด๋ฆ ๋ณ๊ฒฝ ๋ฐ ๊ตฌํ ๋ด์ฉ ์์
...
@Composable
fun NutrientScreen(
...
) {
LaunchedEffect(key1 = true) {
// State ์ด๊ธฐํ
...
// ViewModel๋ก๋ถํฐ ๋ฐ์ ์ด๋ฒคํธ ์ฒ๋ฆฌ
viewModel.nutrientScreenEventFlow.collectLatest { event ->
when (event) {
is NutrientScreenEvent.ShowSnackbar -> {
...
}
is NutrientScreenEvent.RequestScrollToItem -> {
Log.i("interfacer_han", "(์ด๋ฒคํธ ScrollToItem) ์์ (์์ดํ
๊ฐฏ์: ${viewModel.nutrientScreenState.value.dailyMeals.size})")
listState.requestScrollToItem(event.index, listState.firstVisibleItemScrollOffset)
Log.i("interfacer_han", "(์ด๋ฒคํธ ScrollToItem) ๋ (์์ดํ
๊ฐฏ์: ${viewModel.nutrientScreenState.value.dailyMeals.size})")
}
}
}
}
LaunchedEffect(key1 = viewModel.isInitialized.value) {
...
}
LazyColumn(
...
) {
...
}
}
NutrientScreenEvent.ScrollToItem๋ฅผ NutrientScreenEvent.RequestScrollToItem๋ก ๋ฐ๊พผ๋ค. ๊ตฌํ ๋ด์ฉ์ ์๋ listState.scrollToItem()์ ์ญ์ ํ๊ณ requestScrollToItem()์ ๋์ ์ฌ์ฉํ๋ค.
public final fun requestScrollToItem(
@IntRange(from = 0.toLong()) index: Int,
scrollOffset: Int = 0
): Unit
requestScrollToItem()์ ์ฒซ๋ฒ์งธ ์ธ์๋ ํ๋ฉด์ ๋ณด์ผ ์ฒซ๋ฒ์งธ ์์ดํ
, ๋๋ฒ์งธ ์ธ์๋ scrollOffset์ด๋ค. scrollOffset์ '์ผ๋ง๋ ์คํฌ๋กค๋์๋์ง์ ์ ๋๊ฐ'์ด๋ค. scrollOffset์ด ์ฌ์ฉ๋ ์ฝ๋๋ ์ง๊ด์ ์ผ๋ก ์ดํดํ๊ธฐ ์ด๋ ต๋ค. ๋ฐ๋ผ์ ๊ฐ๋จํ๊ฒ ๊ทธ ๊ตฌ์กฐ๋ฅผ ์๋์ ๋จ๊ฒจ๋์ผ๋ ค๊ณ ํ๋ค.
#2-2 scrollOffset ์ ๋ฌด์ ์ฐจ์ด
๋จผ์ , requestScrollToItem()์์ scrollOffset์ด 0์ธ ๊ฒฝ์ฐ์ ๋์๋๋ ์๋์ ๊ฐ๋ค.
์๋ index์ ์ต์๊ฐ์ 0์ด์ง๋ง, ์ง๊ด์ ์ธ ์ดํด๋ฅผ ์ํด ๋์์ด ๋ฎ์์ง ์ ์๋ค๊ณ ๊ฐ์ ํ๋ค. 1 ์์ 2 ์ ์ํ๊ฐ ๋ ์ ๋๋ก ์คํฌ๋กค์ ์งํํ๋ค๊ณ ํด๋ณด์. ์ด๋ฌ๋ฉด, ์ฒซ๋ฒ์งธ ์์ดํ
์ด ํ๋ฉด์ ๋
ธ์ถ๋ ์ํ๊ธฐ ๋๋ฌธ์ ๋ฌดํ ์คํฌ๋กค ๋ก์ง์ด ์๋ํด 3 ์ ์ํ๊ฐ ๋๋ค. ํ๋ฉด์ด 2 ์์ ๊ฐ์๊ธฐ 3 ์ด ๋๋ฉด ๋ญ๊ฐ ๋๋ ๋์ด์ง๋ ๋๋์ด ๋ ๊ฒ์ด๋ค.
๋ฐ๋ฉด, requestScrollToItem()์์ scrollOffset์ด 0์ด ์๋ ๊ฒฝ์ฐ๋ ์๋ค. #2-1์์ ์ฌ์ฉ๋ scrollOffset์ LazyListState.firstVisibleItemScrollOffset๋ก, ํ๋ฉด์ ๋ณด์ด๋ ์ฒซ๋ฒ์งธ ์์ดํ
์ด ์ผ๋ง๋ ์คํฌ๋กค๋์๋์ง๋ฅผ ์๋ฏธํ๋ ๊ฐ์ด๋ค. ์ด ๊ฒฝ์ฐ์ ๋์๋๋ ์๋์ ๊ฐ๋ค.
2 ์์ 3 ์ผ๋ก ๊ฐ ๋, index 0์ด ํ๋ฉด์ ๋งจ ์์์ ์คํฌ๋กค๋ ์ ๋๊ฐ๋งํผ์ ์ถ๊ฐ๋ก ๋ํด์ฃผ๋ฉด 3 ์ด ์๋๋ผ 3' ์ด ๋๋ค. ์ด๋ฌ๋ฉด ์ฌ์ฉ์ ์
์ฅ์์ ์์ดํ
์ด ์ถ๊ฐ๋์์์๋ ๋์ ๋ณด์ด๋ ํ๋ฉด์ ๋ณํจ์์ด ์ฐ์๋ ๊ฒ์ฒ๋ผ ๋ณด์ธ๋ค.
#2-3 Log.i( ... ) ์ ๋ถ ์ญ์
ํ๋ก์ ํธ์ ์๋ Log.i( ... )๋ฅผ ์ ๋ถ ์ญ์ ํ๋ค. ๋ด๊ฐ ์ํ๋ ์์ค์ ์ญ๋ฐฉํฅ ๋ฌดํ ์คํฌ๋กค ๊ตฌํ์ ์ํด์ , ํจ์ ๊ฐ ์คํ ์์๋ฅผ ์ธ์งํ๊ณ ์์ด์ผ ํ๋ค๊ณ ์๊ฐํ๋ค. ๊ทธ๋์ ์ด์ ๊ฒ์๊ธ์์ ๋จ๊ฒจ๋์๋ ๋ก๊ทธ ์ฝ๋์ธ๋ฐ, ๋ณธ ๊ฒ์๊ธ์ ํตํด ์ญ๋ฐฉํฅ ๋ฌดํ ์คํฌ๋กค์ ์์ฃผ ๊น๋ํ๊ฒ ํด๊ฒฐ๋์์ผ๋ ์ด์ ํ์๊ฐ ์๋ค. ๋น์ฐํ๊ฒ ์ง๋ง #2-1์ ์ฝ๋์ ์๋ Log.i( ... )๋ ๋ง์ฐฌ๊ฐ์ง๋ก ์ญ์ ํ๋ค.
#2-4 NutrientViewModelEvent.InitializeState ์ด๋ฒคํธ ๊ตฌํ ์์
...
class NutrientViewModel : ViewModel() {
// (1) ํ๋ฉด ํ์์ฉ State
...
// (2) ViewModel์ฉ ๋ด๋ถ ๋ณ์
...
// (3) View์์ ๋ฐ์ ์ฒ๋ฆฌํ ์ด๋ฒคํธ
...
// (4) View๋ก๋ถํฐ ๋ฐ์ ์ด๋ฒคํธ ์ฒ๋ฆฌ
fun onEvent(event: NutrientViewModelEvent) {
when (event) {
is NutrientViewModelEvent.InitializeState -> {
var dateToInsert = LocalDate.now()
repeat(20) {
_nutrientScreenState.value.dailyMeals.add(
DailyMeal(
date = dateToInsert,
meals = SnapshotStateList()
)
)
dateToInsert = dateToInsert.plusDays(1)
}
viewModelScope.launch {
delay(100)
_isInitialized.value = true
}
}
is NutrientViewModelEvent.LoadMoreItemsAfterLastDate -> {
...
}
is NutrientViewModelEvent.LoadMoreItemsBeforeFirstDate -> {
...
}
}
}
}
_isInitialized.value = true ๊ตฌ๋ฌธ์ ์ฝ๋ฃจํด ์์ญ์ ๋ฃ๊ณ 0.1์ด ์๋ค๊ฐ ์คํ๋๊ฒ ์์ ํ๋ค. ์ด๋ ๊ฒ ์์ ํ์ง ์์ผ๋ฉด, ์ฑ์ ๋งจ ์ฒ์ ์คํ์์ผฐ์ ๋ ํ๋ฉด์์ ์ค๋ ๋ ์ง๊ฐ ์๋๋ผ ๊ทธ ์ ๋ ์ ๋ ์ง๊ฐ ์ฒ์์ผ๋ก ํ์๋๋ค. ์คํฌ๋กค์ ๊ด๋ จํ ์๋ฌ๋ก, requestScrollToItem์ด ์ ๋๋ก ์๋ํ์ง ์์๋ค๋ ๋ง์ด๋ค. ์ถ์ธก์ปจ๋ LazyColumn์ ๋ค์ด๊ฐ ์์ดํ
๋ฆฌ์คํธ์ ์์ดํ
์ ์ถ๊ฐํจ๊ณผ ๋์์ _isInitialized.value์ ๊ฐ์ ๋ณ๊ฒฝํด์, ๋์ ๋ณ๊ฒฝ ์ฌํญ์ด ํ๊บผ๋ฒ์ ๋ค์ ๋ฒ Recomposition์ ๋ฐ์๋์ด ์ผ์ด๋๋ ์๋ฌ๋ก ๋ณด์ธ๋ค. ํด๊ฒฐ์ ์ํด ๋ฐ๋ผ์ ์ฝ๊ฐ์ ๋๋ ์ด๋ฅผ ๋์๋ค. delay(100)์ด ์๋๋ผ delay(1)๋ก ๋์ด๋ ์ ์๋ํ์ง๋ง ์ผ๋จ์ delay(100)๋ผ๊ณ ๋์๋ค. ๊ทธ๋ฆฌ๊ณ ์ด ์ฝ๋๋ฅผ ์์ฑํ๋ฉฐ #3์ ์๋ ๊ฐ์ ๋ฐฉํฅ์ฑ์ด ๋ ์ฌ๋๋ค.
#3 ๊ฐ์ ๋ฐฉ์
๋ฐ๋ก, ViewModel์์ LazyColumn์ด ๋ณด์ ํ ์์ดํ
๋ฆฌ์คํธ๋ฅผ ์กฐ์ํ ์ดํ์ ์คํฌ๋กค ๊ด๋ จ ์ด๋ฒคํธ๋ฅผ View์ ๋ณด๋ด์ฃผ๋ ๊ตฌ์กฐ๊ฐ ๊ทธ๋ ๋ค. Model์ด '์ฌ๋ฃ'๊ณ View๊ฐ '์๋ฆฌ'๋ผ๋ฉด, View Model์ 'ํ์ํ ์ฌ๋ฃ๊ฐ ์ฌ๋ผ๊ฐ์๋ ๋๋ง'๋ค. ViewModel์์๋ ์์ดํ
๋ฆฌ์คํธ์ ๊ด๋ฆฌ๋ง ๋ด๋นํ๊ณ , ์คํฌ๋กค ๊ด๋ จํ ๋์์ View์๊ฒ ์จ์ ํ ๋๊ฒจ์ฃผ์ด์ผ ํ๋ค๊ณ ๋ณธ๋ค. #2-4๋ง ๋ด๋ ๋น์ฅ์ ์๋ฌ๋ฅผ ๊ณ ์น๊ธฐ ์ํ ์๋ฏธ ์๋ delay(100)์ด ์ฌ์ฉ๋์๋๋ฐ, ์ด๋ฐ ๋ฌธ์ ๋ ์๋ชป๋ ๊ตฌ์กฐ์์ ๋์ค๋ ๊ฒ์ผ๋ก ์๊ฐ๋๋ค. ๋น์ฅ ๋ค์ ์์
์ผ๋ก ์ด ๊ตฌ์กฐ์ ๊ฐ์ ์ ํ ์๋ ์์ง๋ง, ํฌ๊ฒ ๊ธํ ๋ถ๋ถ์ ์๋๊ธฐ ๋๋ฌธ์ ๋ค๋ฅธ ์์
์ ๋จผ์ ์งํํ ์๋ ์๊ฒ ๋ค.
#4 ์์ฝ
LazyListState์ ๋ด๊ฐ ์ํ๋ ํจ์๊ฐ ์์๋ค. ๊ณต์ ๋ฌธ์๋ฅผ ์ ์ฝ๋ ์ต๊ด์ ๋ค์ด์. ๋, ์คํฌ๋กค ๊ด๋ จํ ๋์์ View๊ฐ ์จ์ ํ 100% ๋ด๋นํ๊ฒ๋ ๋ง๋ค์ด์ผ ํ ๊ฐ์ ๋ฐฉํฅ์ด ๋ณด์ธ๋ค.
#5 ์์ฑ๋ ์ฑ
#5-1 ์๋ ์์
#5-2 ์ด ๊ฒ์๊ธ ์์ ์ Commit
GitHub - Kanmanemone/nutri-capture-new
Contribute to Kanmanemone/nutri-capture-new development by creating an account on GitHub.
github.com
#5-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 ํ๋ก ํธ์๋ - Card (0) | 2024.10.17 |
---|---|
Nutri Capture ํ๋ก ํธ์๋ - ์คํฌ๋กค ๋ก์ง View์ ์ผ์ (0) | 2024.10.16 |
Nutri Capture ํ๋ก ํธ์๋ - ์ญ๋ฐฉํฅ ๋ฌดํ ์คํฌ๋กค (2) | 2024.10.12 |
Nutri Capture ํ๋ก ํธ์๋ - ๋ฌดํ ์คํฌ๋กค ๋ก์ง ๋ถ๋ฆฌ (0) | 2024.10.12 |
Nutri Capture ํ๋ก ํธ์๋ - NutrientScreen์ ๋ฌดํ ์คํฌ๋กค (1) | 2024.10.11 |