Jetpack Compose字母索引滚动条显示数字而非首字母问题修复
问题原因
- 核心问题是混淆了字符串资源ID和实际显示的字符串内容,所有和条目名称相关的逻辑都错误操作了整型资源ID,而非真实的名称字符串:
- 枚举类
Items中定义的nameId是@StringRes标记的整型常量,是Android编译时生成的资源索引值(本质是整数),不是用户看到的条目名称字符串。 - 生成侧边索引的代码
it.nameId.first().uppercase()直接对整型ID操作,本质是将整数转为十进制字符串后取首位字符,自然得到数字,而非条目名称的首字母。 - 列表排序逻辑
sortedBy { it.nameId }是按资源ID的数字大小排序,不是按条目名称的字母序排序,即使修复首字母显示问题,索引和列表位置也会完全错位。 - 索引点击跳转的匹配逻辑同样基于资源ID判断,会导致跳转位置完全错误。
- 枚举类
修复方法
- 核心原则:所有和条目名称相关的排序、首字母提取、位置匹配逻辑,都必须基于
stringResource()解析出的真实字符串操作,不能直接使用整型资源ID。 - 具体调整步骤:
- 在Compose可组合上下文内,先将所有枚举项和对应的解析后真实名称、类型字符串绑定,避免后续反复操作资源ID。
- 基于真实的条目名称字符串做排序,保证列表顺序和字母索引顺序一致。
- 基于真实名称提取首字母生成索引集合,替换原有基于资源ID的生成逻辑。
- 调整索引跳转的匹配逻辑,同样基于真实名称的首字母做匹配。
修复后的核心实现代码:
enum class Items(@StringRes val nameId: Int, val typeId: Int) { Item1(R.string.apple, R.string.fruit), Item2(R.string.banana, R.string.fruit), Item3(R.string.cherry, R.string.fruit), Item4(R.string.date, R.string.fruit), Item5(R.string.eggplant, R.string.vegetable), Item6(R.string.fig, R.string.fruit), Item7(R.string.guava, R.string.fruit), Item8(R.string.jackfruit, R.string.fruit), Item9(R.string.kiwi, R.string.fruit), Item10(R.string.leek, R.string.vegetable), Item11(R.string.okra, R.string.vegetable), Item12(R.string.pumpkin, R.string.fruit), Item13(R.string.rhubarb, R.string.fruit), Item14(R.string.spinach, R.string.vegetable), Item15(R.string.turnip, R.string.vegetable), Item16(R.string.watermelon, R.string.fruit); } // 原始枚举列表直接取内置values即可,无需手动逐个添加 val originalItems = remember { Items.values() } // 先解析所有条目的真实显示文本,再按名称排序 val items = originalItems.map { item -> Triple( first = item, second = stringResource(id = item.nameId), third = stringResource(id = item.typeId) ) }.sortedBy { it.second } // 基于真实名称生成首字母索引 val headers = remember(items) { items.map { it.second.first().uppercase() }.toSet().toList() } Row { val listState = rememberLazyListState() LazyColumn( state = listState, modifier = Modifier.weight(1f) ) { items(items) { itemWrapper -> Column { Text(text = itemWrapper.second) Text(text = itemWrapper.third) } } } val offsets = remember { mutableStateMapOf<Int, Float>() } var selectedHeaderIndex by remember { mutableStateOf(0) } val scope = rememberCoroutineScope() fun updateSelectedIndexIfNeeded(offset: Float) { val index = offsets .mapValues { abs(it.value - offset) } .entries .minByOrNull { it.value } ?.key ?: return if (selectedHeaderIndex == index) return selectedHeaderIndex = index // 基于真实名称首字母匹配跳转位置 val selectedItemIndex = items.indexOfFirst { it.second.first().uppercase() == headers[selectedHeaderIndex] } scope.launch { listState.scrollToItem(selectedItemIndex) } } Column( verticalArrangement = Arrangement.SpaceEvenly, modifier = Modifier .fillMaxHeight() .background(Color.Green) .pointerInput(Unit) { detectTapGestures { updateSelectedIndexIfNeeded(it.y) } } .pointerInput(Unit) { detectVerticalDragGestures { change, _ -> updateSelectedIndexIfNeeded(change.position.y) } } ) { headers.forEachIndexed { i, header -> Text( header, modifier = Modifier.onGloballyPositioned { offsets[i] = it.boundsInParent().center.y } ) } } }
问题运行效果参考:
内容的提问来源于stack exchange,提问作者wbk727
相关产品推荐
相关产品推荐

