Экран прокручивается вверх (Разбиение на страницы Jetpack Compose)

#android #kotlin #pagination #retrofit #android-jetpack-compose

Вопрос:

Я пытаюсь выполнить разбиение на страницы в своем приложении. Во-первых, я выбираю 20 элементов из Api (ограничение), и каждый раз, когда я прокручиваю вниз до нижней части экрана, это число увеличивается на 20 ( nextPage() ). Однако, когда вызывается эта функция, экран переходит наверх, но я хочу, чтобы он продолжался с того места, на котором остановился. Как я могу это сделать?

Вот мой код:

Экран списка персонажей:

 @Composable fun CharacterListScreen(  characterListViewModel: CharacterListViewModel = hiltViewModel() ) {   val state = characterListViewModel.state.value  val limit = characterListViewModel.limit.value   Box(modifier = Modifier.fillMaxSize()) {  val listState = rememberLazyListState()   LazyColumn(modifier = Modifier.fillMaxSize(), state = listState) {  itemsIndexed(state.characters) { index, character -gt;  characterListViewModel.onChangeRecipeScrollPosition(index)  if ((index   1) gt;= limit) {  characterListViewModel.nextPage()  }  CharacterListItem(character = character)  }  }   if (state.error.isNotBlank()) {  Text(  text = state.error,  color = MaterialTheme.colors.error,  textAlign = TextAlign.Center,  modifier = Modifier  .fillMaxWidth()  .padding(horizontal = 20.dp)  .align(Alignment.Center)  )  }  if (state.isLoading) {  CircularProgressIndicator(modifier = Modifier.align(Alignment.Center))  }  } }  

Characterlistviewмодель:

 @HiltViewModel class CharacterListViewModel @Inject constructor(  private val characterRepository: CharacterRepository ) : ViewModel() {   val state = mutableStateOf(CharacterListState())  val limit = mutableStateOf(20)  var recipeListScrollPosition = 0   init {  getCharacters(limit.value, Constants.HEADER)  }   private fun getCharacters(limit : Int, header : String) {  characterRepository.getCharacters(limit, header).onEach { result -gt;  when(result) {  is Resource.Success -gt; {  state.value = CharacterListState(characters = result.data ?: emptyList())  }  is Resource.Error -gt; {  state.value = CharacterListState(error = result.message ?: "Unexpected Error")  }  is Resource.Loading -gt; {  state.value = CharacterListState(isLoading = true)  }  }  }.launchIn(viewModelScope)  }   private fun incrementLimit() {  limit.value = limit.value   20  }   fun onChangeRecipeScrollPosition(position: Int){  recipeListScrollPosition = position  }   fun nextPage() {  if((recipeListScrollPosition   1) gt;= limit.value) {  incrementLimit()  characterRepository.getCharacters(limit.value, Constants.HEADER).onEach {result -gt;  when(result) {  is Resource.Success -gt; {  state.value = CharacterListState(characters = result.data ?: emptyList())  }  is Resource.Error -gt; {  state.value = CharacterListState(error = result.message ?: "Unexpected Error")  }  is Resource.Loading -gt; {  state.value = CharacterListState(isLoading = true)  }  }  }.launchIn(viewModelScope)  }  }  }  

Состояние списка персонажей:

 data class CharacterListState(  val isLoading : Boolean = false,  var characters : Listlt;Charactergt; = emptyList(),  val error : String = "" )  

Комментарии:

1. Я действительно не уверен, чего вы пытаетесь достичь с помощью этой части: characterListViewModel.onChangeRecipeScrollPosition(index) if ((index 1) gt;= limit) { characterListViewModel.nextPage() }

2. По какой-то конкретной причине вы не используете Paging3 библиотеку?

Ответ №1:

Я думаю, что проблема здесь в том, что вы создаете CharacterListState(isLoading = true) во время загрузки. Это создает объект с пустым списком элементов. Поэтому compose отображает LazyColumn здесь пустое поле, которое сбрасывает состояние прокрутки. Простым решением для этого может быть state.value = state.value.copy(isLoading = true) . Затем, во время загрузки, список элементов может быть сохранен (как и состояние прокрутки).

Комментарии:

1. Да, это сработало действительно здорово. Я понял проблему, большое вам спасибо.

Ответ №2:

Не уверен, правильно ли вы используете состояние LazyList. В вашей модели представления создайте экземпляр LazyListState:

 val lazyListState: LazyListState = LazyListState()  

Передайте это в свой композитный файл и используйте его следующим образом:

 @Composable fun CharacterListScreen(  characterListViewModel: CharacterListViewModel = hiltViewModel() ) {   val limit = characterListViewModel.limit.value   Box(modifier = Modifier.fillMaxSize()) {    LazyColumn(modifier = Modifier.fillMaxSize(), state = characterListViewModel.lazyListState) {  itemsIndexed(state.characters) { index, character -gt;   }  }  } }  

Комментарии:

1. Спасибо за ответ, но это не сработало.

2. Я неправильно понял ваш вопрос. Я обновил свое решение. Вот как я сохраняю состояние списка, и он сохраняет последнюю позицию прокрутки при добавлении дополнительных элементов. В качестве альтернативы вы также можете использовать rememberLazyListState. Другое дело — вам не нужно отслеживать ограничения, чтобы определить, когда загружать больше данных. Это встроено в api подкачки. Вы задаете размер подкачки, и дополнительные данные будут автоматически загружаться, когда пользователь прокручивает страницу вниз.