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

Jetpack Compose:如何在LazyVerticalGrid中居中显示错误项

问题描述

我使用LazyVerticalGrid可组合项展示列表条目,当数据加载出错时,希望在LazyVerticalGrid的中心位置展示带重试按钮的错误提示,以下是我编写的错误项组件和LazyVerticalGrid实现代码:

现有实现代码

ErrorItem 组件

@Composable
fun ErrorItem(
    message: String,
    modifier: Modifier = Modifier,
    onRetryClick: () -> Unit,
) {
    Column(
        modifier = modifier
            .padding(dimensionResource(id = R.dimen.dimen_8)),
        horizontalAlignment = Alignment.CenterHorizontally,
        verticalArrangement = Arrangement.Center
    ) {
        Text(
            text = message,
            maxLines = 1,
            style = MaterialTheme.typography.subtitle2,
            color = MaterialTheme.colors.error,
            modifier = Modifier.padding(vertical = dimensionResource(id = R.dimen.dimen_16))
        )
        Button(
            onClick = onRetryClick,
            shape = CircleShape,
            colors = ButtonDefaults.buttonColors(
                backgroundColor = MaterialTheme.colors.error,
                contentColor = MaterialTheme.colors.onError
            )
        ) {
            Text(text = stringResource(id = R.string.try_again))
        }
    }
}

CompaniesScreen 页面实现

@Composable
fun CompaniesScreen(
    modifier: Modifier = Modifier
) {
    val viewModel: CompaniesScreenViewModel = hiltViewModel()

    val companies: LazyPagingItems<Company> = viewModel.companies.collectAsLazyPagingItems()

    val isLoading = companies.loadState.refresh is LoadState.Loading

    val isError = companies.loadState.refresh is LoadState.Error

    val swipeRefreshState = rememberSwipeRefreshState(isRefreshing = isLoading)

    val listState = rememberLazyGridState()


    Scaffold(modifier = modifier, topBar = { LogoToolbar() }) { padding ->
        SwipeRefresh(indicator = { state, trigger ->
            SwipeRefreshIndicator(
                state = state,
                refreshTriggerDistance = trigger,
                scale = true,
                backgroundColor = MaterialTheme.colors.primary,
                contentColor = MaterialTheme.colors.onPrimary
            )
        }, state = swipeRefreshState, onRefresh = companies::refresh) {
            LazyVerticalGrid(
                state = listState,
                modifier = Modifier
                    .fillMaxSize()
                    .padding(
                        horizontal = dimensionResource(id = R.dimen.dimen_20),
                        vertical = dimensionResource(id = R.dimen.dimen_24)
                    ),
                columns = GridCells.Fixed(2),
                horizontalArrangement = Arrangement.spacedBy(dimensionResource(id = R.dimen.dimen_20)),
                verticalArrangement = Arrangement.spacedBy(dimensionResource(id = R.dimen.dimen_24))
            ) {
                if (!isLoading && !isError) {
                    items(companies.itemCount) { index ->
                        CompanyItem(companyName = companies[index]?.name!!)
                    }
                }
                companies.apply {
                    when {
                        loadState.refresh is LoadState.Loading -> {
                            items(16) {
                                CompanyItem(companyName = "", isLoading = true)
                            }
                        }
                        loadState.refresh is LoadState.Error -> {
                            val e = companies.loadState.refresh as LoadState.Error
                            item(span = {GridItemSpan(2)}) {
                                ErrorItem(
                                    modifier = Modifier.fillMaxSize(), // fillParentMaxSize() 试了没生效
                                    message = e.error.localizedMessage
                                        ?: stringResource(id = R.string.something_went_wrong),
                                    onRetryClick = companies::retry
                                )
                            }
                        }
                    }
                }
            }
        }
    }
}

尝试为ErrorItem添加fillParentMaxSize修饰符后并未实现居中效果,实际运行效果如下:
运行效果截图

需求是让错误项在LazyVerticalGrid的区域内居中显示。


解决方案

fillParentMaxSize()是LazyItemScope专属的扩展修饰符,之前不生效核心是两个问题:一是错误使用fillMaxSize()代替了fillParentMaxSize(),二是span硬编码列数容易出现适配问题,按下面修改即可:

  1. 调整错误状态下的item代码,用fillParentMaxSize()让错误项占满网格当前视口的全部宽高,span用maxLineSpan适配任意列数配置,不需要硬编码列数:
loadState.refresh is LoadState.Error -> {
    val e = companies.loadState.refresh as LoadState.Error
    item(span = { GridItemSpan(maxLineSpan) }) {
        ErrorItem(
            modifier = Modifier.fillParentMaxSize(),
            message = e.error.localizedMessage
                ?: stringResource(id = R.string.something_went_wrong),
            onRetryClick = companies::retry
        )
    }
}
  1. 不需要修改ErrorItem的原有逻辑,你已经在Column里配置了verticalArrangement = Arrangement.Center和horizontalAlignment = Alignment.CenterHorizontally,当父布局(ErrorItem的根modifier)占满全部可用尺寸后,会自动把内部文本和按钮放到水平+垂直居中的位置。

注意:不要在Lazy列表的item里使用fillMaxSize(),这个修饰符会触发Lazy布局的无限测量异常,fillParentMaxSize()才是Compose专门为Lazy列表项设计的API,作用是让当前item占满列表可见区域的全部宽高。如果之前试fillParentMaxSize()没生效,检查是不是导入了错误的包,或者把修饰符写在了item作用域的外面。


内容的提问来源于stack exchange,提问作者Jarnojr

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.28 13:51:30