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硬编码列数容易出现适配问题,按下面修改即可:
- 调整错误状态下的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 ) } }
- 不需要修改ErrorItem的原有逻辑,你已经在Column里配置了
verticalArrangement = Arrangement.Center和horizontalAlignment = Alignment.CenterHorizontally,当父布局(ErrorItem的根modifier)占满全部可用尺寸后,会自动把内部文本和按钮放到水平+垂直居中的位置。
注意:不要在Lazy列表的item里使用
fillMaxSize(),这个修饰符会触发Lazy布局的无限测量异常,fillParentMaxSize()才是Compose专门为Lazy列表项设计的API,作用是让当前item占满列表可见区域的全部宽高。如果之前试fillParentMaxSize()没生效,检查是不是导入了错误的包,或者把修饰符写在了item作用域的外面。
内容的提问来源于stack exchange,提问作者Jarnojr
相关产品推荐
相关产品推荐

