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

Jetpack Compose如何检测滚动列中与箭头同Y轴的可组合项

功能需求

需要实现宝可梦进化链视图中,箭头上方的关联图片随箭头当前指向的宝可梦条目动态切换,视图本身支持双向滚动:水平滚动查看下一进化阶段,垂直滚动查看同阶段的进化变种。
具体交互规则:

  • 初始状态箭头默认指向 Vaporeon
  • 用户垂直滚动列表到Jolteon(黄色条目)位置时,箭头开始指向Jolteon的瞬间,上方图片立刻切换为Jolteon对应的关联图片
  • 该图片保持显示,直到箭头指向Jolteon之外的其他条目
  • 箭头指向新条目时,重复上述切换逻辑更新对应图片
现有基础代码
@Composable
private fun EvolutionChainContent(
    parentPokemonDetails: PokemonDetails,
    pokemonEvolutionSteps: List<EvolutionStep>,
    navigateToPokemonDetails: (String) -> Unit
) {
    val commonScrollState = rememberScrollState()
    Row(
        modifier = Modifier
            .horizontalScroll(rememberScrollState()),
        verticalAlignment = Alignment.Bottom
    ) {
        pokemonEvolutionSteps.forEach { evolutionStep ->
            val columnScrollState =
                if (evolutionStep.pokemonDetailList.hasOneItem()) rememberScrollState()
                else commonScrollState
            if (pokemonEvolutionSteps.isNotFirstElement(evolutionStep)) {
                Column(
                    horizontalAlignment = Alignment.CenterHorizontally,
                    verticalArrangement = Arrangement.Bottom,
                    modifier = Modifier
                        .padding(bottom = 115.dp)
                ) {
                    // TODO 替换为箭头当前指向的宝可梦对应内容
                    EvolutionRuleView(evolutionStep.pokemonEvolutionDetails.first())
                    Spacer(modifier = Modifier.height(5.dp))
                    ArrowToNextEvolution(parentPokemonDetails.types.first().getTypeColor())
                }
            }
            Column(
                modifier = Modifier
                    .height(250.dp)
                    .drawVerticalScrollbar(columnScrollState)
                    .verticalScroll(columnScrollState)
            ) {
                evolutionStep.pokemonDetailList.forEachIndexed { index, it ->
                    PokemonItem(
                        pokemonDetails = it,
                        modifier = Modifier
                            .width(155.dp)
                            .height(230.dp),
                        onClick = if (parentPokemonDetails.name != it.name) {
                            { navigateToPokemonDetails(it.name) }
                        } else null
                    )
                    if (evolutionStep.pokemonDetailList.isLastElement(it))
                        Spacer(modifier = Modifier.height(10.dp))
                }
            }
        }
    }
}
已尝试方案与问题
  • 使用onGlobalPosition()方法实现,无法精准完成Y轴位置的判断控制
  • 尝试用LazyColumn替换现有Column实现,因根布局已存在垂直滚动逻辑引发报错
实现方案

不需要替换现有布局组件,也不需要复杂的全局位置监听,直接基于现有固定布局尺寸和ScrollState做计算即可,精度可达像素级:

  1. 先确定固定参考值:右侧宝可梦列表容器高度固定为250dp,箭头所在Column设置了115dp的底部内边距,和列表底部对齐,因此箭头相对于列表容器的Y轴固定锚点偏移为135dp;每个宝可梦条目固定高度230dp,所有尺寸都是硬编码固定值,不需要动态测量。
  2. 基于列表的滚动状态实时计算当前箭头指向的条目索引,代码如下:
// 在evolutionStep循环内,columnScrollState定义之后添加
val density = LocalDensity.current
// 预计算固定尺寸对应的像素值
val arrowAnchorPx = with(density) { 135.dp.roundToPx() }
val itemHeightPx = with(density) { 230.dp.roundToPx() }
// 监听滚动状态,自动计算当前指向的条目索引
val currentTargetIndex by remember {
    derivedStateOf {
        val currentScroll = columnScrollState.value
        val hitPosition = currentScroll + arrowAnchorPx
        (hitPosition / itemHeightPx)
            .coerceIn(0, evolutionStep.pokemonDetailList.lastIndex)
    }
}
// 索引变化时更新箭头上方的图片/进化规则
val currentTargetPokemon = evolutionStep.pokemonDetailList[currentTargetIndex]
// 将currentTargetPokemon传入EvolutionRuleView替换原来的固定取值即可

注意:多进化阶段共用commonScrollState的逻辑无需修改,每个阶段的列会基于同一个滚动值独立计算指向的条目,不会出现错位;该方案无额外布局测量开销,滚动时不会出现性能问题,切换时机完全和箭头指向同步。

内容的提问来源于stack exchange,提问作者Arkadiusz Mądry

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 12:15:30