能否使用Jetpack Compose实现嵌套粘性头部?
Jetpack Compose 实现嵌套粘性头部
Jetpack Compose 官方提供的stickyHeader仅支持单层粘性头部,要实现多层嵌套的粘性效果,需要通过监听滚动状态+自定义悬浮头部的方式来实现。以下是具体实现方案:
1. 定义数据结构
先定义对应层级的数据模型,方便构建列表内容:
data class Category( val title: String, val subCategories: List<SubCategory> ) data class SubCategory( val title: String, val items: List<String> )
2. 核心实现代码
通过LazyColumn构建列表,结合滚动状态监听动态更新悬浮的嵌套头部:
@Composable fun NestedStickyHeaderScreen() { // 模拟测试数据 val categories = listOf( Category( "一级分类A", listOf( SubCategory("二级分类A1", listOf("项目1", "项目2", "项目3")), SubCategory("二级分类A2", listOf("项目4", "项目5")) ) ), Category( "一级分类B", listOf( SubCategory("二级分类B1", listOf("项目6", "项目7")), SubCategory("二级分类B2", listOf("项目8", "项目9", "项目10")) ) ) ) val listState = rememberLazyListState() // 记录当前需要悬浮的一级/二级头部 val currentStickyCategory = remember { mutableStateOf<Category?>(null) } val currentStickySubCategory = remember { mutableStateOf<SubCategory?>(null) } // 监听滚动位置,更新悬浮头部 LaunchedEffect(listState) { snapshotFlow { listState.firstVisibleItemIndex } .collect { index -> var currentIndex = index var targetCategory: Category? = null var targetSubCategory: SubCategory? = null // 遍历数据,匹配当前滚动位置对应的层级头部 for (cat in categories) { if (currentIndex == 0) { targetCategory = cat break } currentIndex-- for (subCat in cat.subCategories) { if (currentIndex == 0) { targetCategory = cat targetSubCategory = subCat break } currentIndex-- if (currentIndex < subCat.items.size) { targetCategory = cat targetSubCategory = subCat break } currentIndex -= subCat.items.size } if (targetCategory != null) break } currentStickyCategory.value = targetCategory currentStickySubCategory.value = targetSubCategory } } Box { // 主列表 LazyColumn(state = listState) { categories.forEach { category -> // 一级头部项 item(key = "category_${category.title}") { CategoryHeader(category.title) } category.subCategories.forEach { subCategory -> // 二级头部项 item(key = "subcategory_${subCategory.title}") { SubCategoryHeader(subCategory.title) } // 子列表项 items(subCategory.items) { item -> ListItemContent(item) } } } } // 悬浮的嵌套粘性头部 Column { currentStickyCategory.value?.let { CategoryHeader(it.title) } currentStickySubCategory.value?.let { SubCategoryHeader(it.title) .padding(start = 16.dp) // 缩进区分层级 } } } } // 一级头部样式 @Composable private fun CategoryHeader(title: String) { Text( text = title, fontSize = 20.sp, fontWeight = FontWeight.Bold, modifier = Modifier .fillMaxWidth() .background(Color.LightGray) .padding(16.dp) ) } // 二级头部样式 @Composable private fun SubCategoryHeader(title: String) { Text( text = title, fontSize = 16.sp, fontWeight = FontWeight.SemiBold, modifier = Modifier .fillMaxWidth() .background(Color.Gray.copy(alpha = 0.8f)) .padding(horizontal = 16.dp, vertical = 12.dp) ) } // 列表项内容 @Composable private fun ListItemContent(text: String) { Text( text = text, modifier = Modifier .fillMaxWidth() .padding(16.dp) ) }
3. 进阶优化:头部顶起动画
如果需要实现演示中"二级头部顶起一级头部"的平滑动画,可以通过记录每个头部的位置坐标,计算偏移量来调整悬浮头部的位置:
- 给每个头部添加
onGloballyPositioned监听,记录其在列表中的Y轴坐标 - 在滚动监听中计算当前悬浮头部的偏移值,通过
Modifier.offset(y = ...)实现动态位移 - 利用
animateDpAsState实现平滑的过渡动画
内容的提问来源于stack exchange,提问作者Akshdeep Singh
相关产品推荐
相关产品推荐

