You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何结合分页API使用LazyVerticalGrid并实现滚动加载与连续填充?

Solution

To implement infinite scrolling with proper grid layout continuation for odd-length pages, follow these steps:

1. ViewModel for State & Pagination Logic

Create a ViewModel to manage the item list, pagination state, and network requests. This keeps state intact across configuration changes and separates business logic from the UI.

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.rx2.await

class ItemViewModel(private val itemRepository: ItemRepository) : ViewModel() {
    // Holds the full list of items to render
    private val _items = MutableStateFlow(emptyList<Item>())
    val items: StateFlow<List<Item>> = _items

    // Tracks if we're currently loading the next page
    private val _isLoading = MutableStateFlow(false)
    val isLoading: StateFlow<Boolean> = _isLoading

    // Tracks if there are more pages to load
    private val _hasMore = MutableStateFlow(true)
    val hasMore: StateFlow<Boolean> = _hasMore

    private var currentPage = 1

    fun loadNextPage() {
        // Prevent duplicate requests or loading when no more pages exist
        if (_isLoading.value || !_hasMore.value) return

        _isLoading.value = true
        viewModelScope.launch {
            try {
                val newItems = itemRepository.getItems(currentPage).await()
                if (newItems.isEmpty()) {
                    // No more items to load
                    _hasMore.value = false
                } else {
                    // Append new items to the existing list
                    _items.update { it + newItems }
                    currentPage++
                }
            } catch (e: Exception) {
                // Handle error (e.g., show snackbar, log)
            } finally {
                _isLoading.value = false
            }
        }
    }
}

// Assume this is your repository class
interface ItemRepository {
    fun getItems(pageNumber: Int): Single<List<Item>>
}

2. Composable with Infinite Scrolling & Grid Layout

Update the ItemGridView to use the ViewModel, detect scroll position, and render items correctly. The LazyVerticalGrid will automatically fill empty grid spots for odd-length pages when new items are appended.

import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.foundation.lazy.grid.rememberLazyGridState
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch

@Composable
fun ItemGridView(viewModel: ItemViewModel = viewModel()) {
    val items by viewModel.items.collectAsState()
    val isLoading by viewModel.isLoading.collectAsState()
    val hasMore by viewModel.hasMore.collectAsState()
    val listState = rememberLazyGridState()
    val coroutineScope = rememberCoroutineScope()

    // Detect when user scrolls to the last item to trigger next page load
    LaunchedEffect(listState) {
        snapshotFlow {
            val lastVisibleIndex = listState.layoutInfo.visibleItemsInfo.lastOrNull()?.index
            lastVisibleIndex == items.size - 1
        }.collectLatest { isAtLastItem ->
            if (isAtLastItem) {
                coroutineScope.launch {
                    viewModel.loadNextPage()
                }
            }
        }
    }

    LazyVerticalGrid(
        columns = GridCells.Fixed(2),
        modifier = Modifier.fillMaxSize(),
        state = listState,
        contentPadding = PaddingValues(8.dp),
        verticalArrangement = Arrangement.spacedBy(8.dp),
        horizontalArrangement = Arrangement.spacedBy(8.dp)
    ) {
        // Render all items
        items(items) { item ->
            item.toPrettyComposableView()
        }

        // Show loading indicator when loading next page
        if (isLoading) {
            item(span = { GridItemSpan(2) }) {
                CircularProgressIndicator(
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(16.dp)
                        .align(Alignment.Center)
                )
            }
        }

        // Show end message when no more items exist
        if (!hasMore && items.isNotEmpty()) {
            item(span = { GridItemSpan(2) }) {
                Text(
                    text = "No more items available",
                    modifier = Modifier
                        .fillMaxWidth()
                        .padding(16.dp),
                    textAlign = TextAlign.Center
                )
            }
        }
    }
}

Key Details Explained

  • Grid Layout Continuation: By appending new items to the existing list, LazyVerticalGrid automatically fills empty grid spots (from odd-length pages) before starting new rows. For example, if the first page has 3 items, the 4th item (from the next page) will occupy the empty second column in the second row.

  • Scroll Detection: Uses snapshotFlow to observe the LazyGridState's visible items. When the last item becomes visible, it triggers loadNextPage().

  • Loading State Management: The isLoading flag prevents duplicate network requests, and the loading indicator spans both grid columns for a clean UI.

  • End of Pagination: The hasMore flag stops further requests when no more items are returned from the API, and shows a message to the user.

Dependencies

To use await() with RxJava Single, add this coroutines dependency to your module-level build.gradle:

implementation "org.jetbrains.kotlinx:kotlinx-coroutines-rx2:1.7.3"

Content of the question来源于stack exchange,提问作者gongie

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 09:00:51