Jetpack Compose animateScrollTo 到绝对坐标或直接到元素

Jetpack Compose animateScrollTo to absolute coordinates or direct to element

为了在撰写中滚动,我使用 scrollState.animateScrollTo() 并通过 onGloballyPositioned {positionInRoot/positionInParent} 获取坐标元素。

animateScrollTo 按偏移值移动。所以如果我们在使用 animateScrollTo 时不在屏幕顶部,那么我们有一个间隙和移动后元素不在屏幕顶部。

我们可以,先滚动到顶部,然后滚动到元素的坐标:

coroutineScope.launch {
    scrollState.scrollTo(0)
    delay(50)
    scrollState.animateScrollTo(sectionTwoCoordinates.roundToInt())
}

但是我不喜欢。

所以也许我们可以获得所有元素的绝对坐标,然后滚动到它们。或者以某种方式将 id 设置为元素并按 ID 滚动。 我也不能在主项目中使用 LazyColumn,所以需要不基于它的解决方案

@Composable
fun ScrollSections() {
    val coroutineScope = rememberCoroutineScope()
    val scrollState = rememberScrollState()


    var sectionOneCoordinates by remember { mutableStateOf(0f) }
    var sectionTwoCoordinates by remember { mutableStateOf(0f) }
    var sectionThreeCoordinates by remember { mutableStateOf(0f) }

    var showSectionTwo by remember { mutableStateOf(false) }

    Column(
        Modifier
            .fillMaxWidth()
            .background(Color.White)
            .verticalScroll(scrollState)
    ) {
        Spacer(modifier = Modifier.height(400.dp))
        Row(modifier = Modifier.fillMaxWidth()) {
            Button(
                onClick = {
                    coroutineScope.launch {
                        scrollState.animateScrollTo(
                            sectionOneCoordinates.roundToInt()
                        )
                    }
                },
                modifier = Modifier.weight(0.33f)
            ) {
                Text(text = "1")
            }
            Spacer(modifier = Modifier.width(4.dp))
            Button(
                onClick = {
                    coroutineScope.launch {
//                        scrollState.scrollTo(
//                            0
//                        )
//                        delay(50)
                        scrollState.animateScrollTo(
                            sectionTwoCoordinates.roundToInt()
                        )
                    }
                },
                modifier = Modifier.weight(0.33f)
            ) {
                Text(text = "2")
            }
            Spacer(modifier = Modifier.width(4.dp))
            Button(
                onClick = {
                    coroutineScope.launch {
                        scrollState.animateScrollTo(
                            sectionThreeCoordinates.roundToInt()
                        )
                    }
                },
                modifier = Modifier.weight(0.33f)
            ) {
                Text(text = "3")
            }
        }
        Spacer(modifier = Modifier.height(12.dp))
        Button(onClick = { showSectionTwo = !showSectionTwo }) {
            Text(text = "Show Section 2")
        }
        Spacer(modifier = Modifier.height(50.dp))


        Column(modifier = Modifier
            .fillMaxWidth()
            .onGloballyPositioned { layoutCoordinates ->
                sectionOneCoordinates = layoutCoordinates.positionInRoot().y
            }
        ) {
            Text(text = "Section 1", fontSize = 32.sp)
            Box(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(200.dp)
                    .background(test1),
                contentAlignment = Alignment.Center
            ) {
                Text("subsection 1")
            }
            Spacer(modifier = Modifier.height(4.dp))
            Box(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(200.dp)
                    .background(test2), contentAlignment = Alignment.Center
            ) {
                Text("subsection 2")
            }
            Spacer(modifier = Modifier.height(4.dp))
            Box(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(200.dp)
                    .background(test3), contentAlignment = Alignment.Center
            ) {
                Text("subsection 3")
            }
            Spacer(modifier = Modifier.height(4.dp))
            Button(
                onClick = {
                    coroutineScope.launch {
                        scrollState.animateScrollTo(0)
                    }
                },
                modifier = Modifier.fillMaxWidth()
            ) {
                Text(text = "Go UP")
            }
        }
        Spacer(modifier = Modifier.height(24.dp))
        if (showSectionTwo) {
            Column(modifier = Modifier
                .fillMaxWidth()
                .onGloballyPositioned { layoutCoordinates ->
                    sectionTwoCoordinates = layoutCoordinates.positionInRoot().y
                }
            ) {
                Text(text = "Section 2", fontSize = 32.sp)
                Box(
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(200.dp)
                        .background(test4), contentAlignment = Alignment.Center
                ) {
                    Text("subsection 1")
                }
                Spacer(modifier = Modifier.height(4.dp))
                Box(
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(200.dp)
                        .background(test5), contentAlignment = Alignment.Center
                ) {
                    Text("subsection 2")
                }
                Spacer(modifier = Modifier.height(4.dp))
                Box(
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(200.dp)
                        .background(test6), contentAlignment = Alignment.Center
                ) {
                    Text("subsection 3")
                }
                Spacer(modifier = Modifier.height(4.dp))
                Button(
                    onClick = {
                        coroutineScope.launch {
                            scrollState.animateScrollTo(0)
                        }
                    },
                    modifier = Modifier.fillMaxWidth()
                ) {
                    Text(text = "Go UP")
                }
            }
        }
        Spacer(modifier = Modifier.height(24.dp))
        Column(modifier = Modifier
            .fillMaxWidth()
            .onGloballyPositioned { layoutCoordinates ->
                sectionThreeCoordinates = layoutCoordinates.positionInRoot().y
            }
        ) {
            Text(text = "Section 3", fontSize = 32.sp)
            Box(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(200.dp)
                    .background(test8),
                contentAlignment = Alignment.Center
            ) {
                Text("subsection 1")
            }
            Spacer(modifier = Modifier.height(4.dp))
            Box(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(200.dp)
                    .background(test7),
                contentAlignment = Alignment.Center
            ) {
                Text("subsection 2")
            }
            Spacer(modifier = Modifier.height(4.dp))
            Box(
                modifier = Modifier
                    .fillMaxWidth()
                    .height(200.dp)
                    .background(test9),
                contentAlignment = Alignment.Center
            ) {
                Text("subsection 3")
            }
            Spacer(modifier = Modifier.height(4.dp))
            Button(
                onClick = {
                    coroutineScope.launch {
                        scrollState.animateScrollTo(0)
                    }
                },
                modifier = Modifier.fillMaxWidth()
            ) {
                Text(text = "Go UP")
            }
        }
    }
}

这里需要根据parent position

得到Coordinates

所以你需要使用:

layoutCoordinates.positionInParent()

而不是

layoutCoordinates.positionInRoot()

示例:

Column(modifier = Modifier
     .fillMaxWidth()
     .onGloballyPositioned
     { layoutCoordinates ->
          sectionThreeCoordinates = ayoutCoordinates.positionInParent().y
     }
)
{ /* content */ }