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

Jetpack Compose中Column如何获取首尾可见项(无法使用LazyColumn)

获取Jetpack Compose Column中首尾可见小时项的方案

因为和自定义布局共享滚动状态导致无法使用LazyColumn,要获取Column里7天小时项的首尾可见项,可以通过监听每个项的布局位置+滚动状态计算来实现,具体步骤和修改后的代码如下:

1. 核心思路

  • 给每个小时项添加布局位置监听,判断其是否处于滚动视图的可见范围内
  • 通过滚动状态ScrollState跟踪滚动位置,每次滚动时重置可见项状态,重新计算首尾可见项
  • 用MutableState保存首尾可见的时间值

2. 修改后的完整代码

ScheduleSidebar 组件

@Composable
fun ScheduleSidebar(
    hourHeight: Dp,
    scrollState: ScrollState,
    modifier: Modifier = Modifier,
    label: @Composable (time: LocalTime) -> Unit = { HourLabel(time = it) },
) {
    val startTime = LocalTime.MIN
    val firstVisibleTime = remember { mutableStateOf<LocalTime?>(null) }
    val lastVisibleTime = remember { mutableStateOf<LocalTime?>(null) }
    val density = LocalDensity.current

    // 每次滚动时重置首尾状态,确保重新计算
    LaunchedEffect(scrollState.value) {
        firstVisibleTime.value = null
        lastVisibleTime.value = null
    }

    Column(
        modifier = modifier
            .verticalScroll(scrollState)
    ) {
        repeat(7) { day ->
            repeat(24) { hour ->
                val currentTime = startTime.plusHours(hour.toLong())
                Box(
                    modifier = Modifier
                        .height(hourHeight)
                        .onGloballyPositioned { coordinates ->
                            val scrollY = scrollState.value
                            val viewportHeight = with(density) { scrollState.layoutInfo.viewportSize.height.toDp().toPx().toInt() }
                            val itemTop = coordinates.positionInParent().y.toInt()
                            val itemBottom = itemTop + coordinates.size.height

                            // 判断当前项是否在可见视口内
                            val isVisible = itemBottom > scrollY && itemTop < scrollY + viewportHeight
                            if (isVisible) {
                                // 更新第一个可见项:取最早的时间
                                if (firstVisibleTime.value == null || currentTime.isBefore(firstVisibleTime.value)) {
                                    firstVisibleTime.value = currentTime
                                }
                                // 更新最后一个可见项:取最晚的时间
                                if (lastVisibleTime.value == null || currentTime.isAfter(lastVisibleTime.value)) {
                                    lastVisibleTime.value = currentTime
                                }
                            }
                        }
                ) {
                    label(currentTime)
                }
            }
        }
    }

    // 示例:打印首尾可见时间(可根据需求替换为业务逻辑)
    LaunchedEffect(firstVisibleTime.value, lastVisibleTime.value) {
        println("第一个可见时间:${firstVisibleTime.value}")
        println("最后一个可见时间:${lastVisibleTime.value}")
    }
}

HourLabel 组件(修复文本格式化问题)

@Composable
fun HourLabel(
    time: LocalTime,
    modifier: Modifier = Modifier,
) {
    val timeText = remember(time) {
        time.format(DateTimeFormatter.ofPattern("HH:mm"))
    }
    Text(
        text = timeText,
        modifier = modifier
            .fillMaxHeight()
            .padding(4.dp)
    )
}

3. 使用说明

  • 调用ScheduleSidebar时,需要传入和自定义布局共享的ScrollState实例,比如:
    val sharedScrollState = rememberScrollState()
    ScheduleSidebar(
        hourHeight = 60.dp,
        scrollState = sharedScrollState
    )
    // 自定义布局同样使用 sharedScrollState
    
  • firstVisibleTime和lastVisibleTime保存了当前首尾可见的小时时间,可根据业务需求将其用于同步其他布局、显示当前时间范围等场景。

内容的提问来源于stack exchange,提问作者EliodeBeirut

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 02:55:26