Jetpack Compose之HorizontalPager嵌套动态数量的LazyColumn+Paging3
对于分类展示结构化数据的场景,一般使用Tab+Pager+List的形式。当tab的数量固定时比较好解决,最基础的方式是写n个PagingData和LazyListState:
val recommendState = rememberLazyListState()
val recommendData = uiState.recommendPager.collectAsLazyPagingItems()
val textState = rememberLazyListState()
val textData = uiState.textPager.collectAsLazyPagingItems()
val imageState = rememberLazyListState()
val imageData = uiState.imagePager.collectAsLazyPagingItems()
Column(modifier = Modifier.fillMaxSize()) {
JokeTabs(
tabs = uiState.tabs.map { it.source },
selected = uiState.selectedTab.source,
onClick = {
val index = uiState.tabs.map { it.source }.indexOf(it)
scope.launch { pagerState.animateScrollToPage(index) }
}
)
HorizontalPager(count = uiState.tabs.size, state = pagerState) { page ->
when (page) {
0 -> JokeList(
items = recommendData, state = recommendState,
headerClickListener = headerClickListener,
contentClickListener = contentClickListener,
infoClickListener = infoClickListener
)
1 -> JokeList(
items = textData, state = textState,
headerClickListener = headerClickListener,
contentClickListener = contentClickListener,
infoClickListener = infoClickListener
)
2 -> JokeList(
items = imageData, state = imageState,
headerClickListener = headerClickListener,
contentClickListener = contentClickListener,
infoClickListener = infoClickListener
)
else -> {}
}
}
}
但是这种方法很笨重,需要写很多重复代码,而且无法适应动态数量Tab的形式。解决办法是将LazyColumn的State和PagingData的State放在ViewModel中,而不是放在ui中。
首先,创建UiState:
data class JokeListState(
val listState: LazyListState,
val swipeRefreshState: SwipeRefreshState,
val pagingData: Flow<PagingData<JokeData>>
)
然后在ViewModel中初始化UiState。UiState中三个对象的初始化方法在JetpackCompose提供的remember方法中,没有internal方法,可以直接拿来用。
最后,在ui中引用该UiState:
val owner = LocalLifecycleOwner.current
val uiState by vm.uiState.collectAsStateWithLifecycle(lifecycleOwner = owner)
// Pager中的页面
@Composable
private fun JokeList(
state: JokeListState,
modifier: Modifier = Modifier,
headerClickListener: JokeHeaderClickListener,
contentClickListener: JokeContentClickListener,
infoClickListener: JokeInfoClickListener
) {
val pagingData = state.pagingData.collectAsLazyPagingItems()
val listState = rememberSaveable(saver = LazyListState.Saver) { state.listState } // 注意使用rememberSaveable保存LazyColumn的状态
SwipeRefresh(state = state.swipeRefreshState, onRefresh = { pagingData.refresh() }) {
LazyColumn(modifier = modifier, state = listState) {
items(pagingData) {
it?.let {
JokeCard(
joke = it,
headerClickListener = headerClickListener,
contentClickListener = contentClickListener,
infoClickListener = infoClickListener
)
}
}
}
}
}
以上需要注意的点是需要使用rememberSaveable保存LazyColumn的滑动状态,否则切换Pager的时候会清空该状态