Kotlin Unendliches Scrollen nach unten und oben mit Jetpack Compose und Kotlin

Huri57

Cadet 2nd Year
Registriert
Mai 2020
Beiträge
23
Was ich erreichen möchte:
Ich möchte einen monatlichen Kalenderscreen, der unendlich scrollbar ist und beim aktuellen Monat beginnt. Also rufe ich die Funktion previousPage() auf, die das nächste Element über das jetzige platziert, indem ich das Element an Index 0 der Liste, die displayed wird, einfüge.
1630228886973.png


Was funktioniert
Das Runterscrollen in Richtung Zukunft funktioniert wie gewollt: Sobald man ein neues Jahr betritt, wird das Folgejahr in die Liste geladen, sodass man unendlich scrollen kann. Einziges Problem ist, dass es zwischen den Jahren lagt.

Was das Problem ist:
Sobald ein Element am Anfang der Liste eingefügt wird, springt man auch direkt zu diesem Element. Das liegt wohl daran das kurz vor Einfügen des neuen Elements an Index 0, der Index des aktuellen Elements auch 0 ist, sodass LazyColumn direkt zum aktuellsten Index 0 scrollt. Dabei ist die gewollte Position nach dem Einfügen der Index 1. Das große Problem besteht darin, dass man in eine Endlosschleife kommt, da man immer direkt zum Index von 0 gescrollt wird und der Index auch zugleich der Trigger für das Laden des nächsten Elements ist.

Was mein Code ist:
Die Liste besteht aus eine data class namens CYear, die eine Hierarchie einleitet:
Code:
data class CYear (val months: List<CMonth>, val year: Int)

data class CMonth(val month: Month, val days: MutableList<CDay>)

data class CDay (val day: Byte, val clickable: Boolean)

Hier wäre die Haupt-Composable function die auch nextYear() und previousYear() aufruft:
Code:
@Composable

fun Calendar ( viewModel: CalendarViewModel) {
val page = viewModel.page
val coroutineScope = rememberCoroutineScope()
val state = rememberLazyListState()
LazyColumn (modifier = Modifier.padding(start = 8.dp, end = 8.dp), state = state) {
items(
items = viewModel.CYear//list to be displayed
) { year ->
/*if (viewModel.firstInitialize()) {
coroutineScope.launch {
state.scrollToItem(viewModel.initializeScrollState.value)
}
}*/ //Commented it out, this should scroll at app-launch to the currYear at index 1;
// I think that's not really great; it would be better, if I hadn't to scroll programmatically
//; greater would be something like an initial startPoint of index 1

for (i in year.months) {//loop over the months of one year
FullMonth(month = i) {// display one month
}
}
val scrollIndex = state.firstVisibleItemIndex//track the scrollPosition
viewModel.onChangeCurrentScrollIndex(scrollIndex)//set the currScrollPosition
if (scrollIndex+1 > page.value) {// Deduplication
Log.i("Calendar", "scrollYear: $scrollIndex")
viewModel.nextPage(scrollIndex)//call nextPage()
}
// if(scrollIndex == 0) {
// viewModel.previousPage(scrollIndex)}// call previousPage() | doesn't work
}
}
}

Hier ist noch die Funktion, um eine neues Jahr der Zukunft einzufügen:
Code:
fun nextPage (currScrollIndex: Int) {

if( currScrollIndex+1 > page.value && currScrollYearState.value > currYear) {
page.value = page.value+1
viewModelScope.launch {//coroutine, but somehow it lags between the years nevertheless; Has somebody a tip, why the main thread is blocked between the years?
CYear.add(createOneYear(currScrollYearState.value+1))
Log.i("Calendar", "NextPage: ${currScrollYearState.value+1}")
}
}
}

Die Funktion, um ein vergangenes Jahr einzufügen ist eigentlich dieselbe nur dass, das if-Statement etw. anders lautet und bei der CYear.add methode (currScrollYearState.value-1) gemacht wird.


Zusammenfassung:
Zusammengefasst ist das Hauptproblem, das man immer zum Index 0 springt, sobald Jahre der Vergangenheit hinzugefügt werden. Das führt nämlich zu einer Endlosschleife, die ganze Zeit weitere Jahre der Vergangenheit hinzufügt. Wie kann ich also die Scroll-Position halten, selbst wenn an Index 0 ein neues Element eingefügt wird?

Ein weiteres Problem ist, dass es zwischen den Jahren lagt beim Runterscrollen. Sollte ich eine Datenbank erstellen, damit weniger Computation-Power notwendig ist? (Eigentlich habe ich Coroutines verwendet, um den Main-Thread freizubekommen, aber irgendwie scheint das nicht so zu wirken). Oder hat jmd. einen anderen Vorschlag als eine Datenbank oder eine Vermutung woran das Problem eigentlich liegen könnte?


Danke für alle Antworten :)
 
Zuletzt bearbeitet:
Zurück
Oben