屏幕滚动到顶部(Jetpack Compose 分页)

Posted

技术标签:

【中文标题】屏幕滚动到顶部(Jetpack Compose 分页)【英文标题】:Screen scrolls to the top (Jetpack Compose Pagination) 【发布时间】:2021-12-31 18:17:46 【问题描述】:

我正在尝试在我的应用程序中进行分页。首先,我从 Api(限制)获取 20 个项目,每次我向下滚动到屏幕底部时,它都会将此数字增加 20(nextPage())。但是,当调用此函数时,屏幕会转到顶部,但我希望它从中断的地方继续。我该怎么做?

这是我的代码:

CharacterListScreen:

@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 ->
                characterListViewModel.onChangeRecipeScrollPosition(index)
                if ((index + 1) >= 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))
        
    

CharacterListViewModel:

@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 ->
            when(result) 
                is Resource.Success -> 
                    state.value = CharacterListState(characters = result.data ?: emptyList())
                
                is Resource.Error -> 
                    state.value = CharacterListState(error = result.message ?: "Unexpected Error")
                
                is Resource.Loading -> 
                    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) >= limit.value) 
                incrementLimit()
                characterRepository.getCharacters(limit.value, Constants.HEADER).onEach result ->
                    when(result) 
                        is Resource.Success -> 
                            state.value = CharacterListState(characters = result.data ?: emptyList())
                        
                        is Resource.Error -> 
                            state.value = CharacterListState(error = result.message ?: "Unexpected Error")
                        
                        is Resource.Loading -> 
                            state.value = CharacterListState(isLoading = true)
                        
                    
                .launchIn(viewModelScope)
            
    


CharacterListState:

data class CharacterListState(
    val isLoading : Boolean = false,
    var characters : List<Character> = emptyList(),
    val error : String = ""
)

【问题讨论】:

我真的不确定你想用这部分实现什么:characterListViewModel.onChangeRecipeScrollPosition(index) if ((index + 1) &gt;= limit) characterListViewModel.nextPage() 您没有使用Paging3库的任何特殊原因? 【参考方案1】:

不确定您是否正确使用了 LazyListState。在您的视图模型中,创建一个 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 ->

            
        
    

【讨论】:

感谢您的回答,但没有成功。 我误解了你的问题。我更新了我的解决方案。这就是我保持列表状态并在添加其他项目时保持最后滚动位置的方式。或者,您也可以使用 rememberLazyListState。另一件事 - 您不需要跟踪限制来确定何时加载更多数据。这是内置在 Paging api 中的。您设置分页大小,当用户滚动到底部时会自动加载更多数据。【参考方案2】:

我认为这里的问题是您在加载时创建了CharacterListState(isLoading = true)。这将创建一个具有空元素列表的对象。所以 compose 在这里渲染一个空的LazyColumn 来重置滚动状态。简单的解决方案可能是state.value = state.value.copy(isLoading = true)。然后,在加载时,可以保留项目列表(滚动状态也是如此)

【讨论】:

是的,效果非常好。我明白了这个问题,非常感谢。

以上是关于屏幕滚动到顶部(Jetpack Compose 分页)的主要内容,如果未能解决你的问题,请参考以下文章

使用 Jetpack Compose 无限加载 Android Paging 3 库而无需滚动

UIScrollView + 在滚动屏幕之前将项目固定到顶部

Jetpack Compose 无限加载列表(滚到底部自动加载更多)

如何修复选项卡单击第一个输入屏幕滚动到顶部。仅在 Chrome 中

选择日期后,jQuery UI datepicker会使屏幕滚动到顶部

Angular 5 在每次路线点击时滚动到顶部