LazyColumn animateScrollBy 什么都不做?

LazyColumn animateScrollBy does nothing?

更新:原始问题后跟根据要求触发它的更新示例代码。

我在惰性列表中有一个按半小时细分的时间列表

例如12:00、12:30

该列表已附加到伴奏寻呼机库中的水平寻呼机

当用户在页面之间滑动时,我需要更新所选时间,并且我希望所选时间始终位于屏幕中央,如果它不靠近 top/bottom 可以不会吧

我想出如何使用提供给我们的方法准确地做到这一点的唯一方法如下。

注意:我之前已经捕获了按钮/列表高度。

  val listState = rememberLazyListState()

  LaunchedEffect(pagerState) {
            snapshotFlow { pagerState.currentPage }.collect { page ->
                if (page != selectedPage) {
                    listState.scrollToItem(0) //Need to be at the top for calc below to work
                    listState.scrollBy(buttonHeight.toFloat() * page - (listVisibleHeight / 2) + buttonHeight)
                }
            }
        } 

现在这完美地工作了。当我从 00:00 开始并向下移动列表时,直到我到达中间才会发生滚动,一旦我到达中间,所选时间就会随着时间的滚动而停留在中间,并保持在中间直到我接近列表的末尾,它开始向下移动。

现在还有一个方法 listState.animateScrollBy(float, animation) 这将是理想的,所以如果你点击了不在中间的东西,就会有一个动画,它必须将它动画回到中间,但是当我使用它时一个绝对没有任何反应?

   listState.animateScrollBy(buttonHeight.toFloat() * page - (listVisibleHeight / 2) + buttonHeight)

我觉得它完全坏了?好像我从未调用过该函数。 0 滚动,即使我尝试添加非默认动画。我是不是做错了什么?

它在 compose 1.2.0-alpha04 和 1.2.0-alpha06 上执行此操作

我可以继续使用 scrollBy,但没有动画,如果用户点击时间 vs 滑动时间,它会有点跳动。


示例,现在我知道为什么它按要求发生了

传呼机的特殊进口

  implementation "com.google.accompanist:accompanist-pager:0.24.5-alpha"
  implementation "com.google.accompanist:accompanist-pager-indicators:0.24.5-alpha"

注意:自从我更新寻呼机以修复早期版本中的崩溃以来,寻呼机本身一直不稳定……它不能完全向右滑动,有时会卡住。不过我还没有仔细研究过。

import android.util.Log
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.animateScrollBy
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.onGloballyPositioned
import com.google.accompanist.pager.ExperimentalPagerApi
import com.google.accompanist.pager.HorizontalPager
import com.google.accompanist.pager.PagerState

@ExperimentalPagerApi
@Composable
fun TestScreen() {
    val numberOfRows = 48

    val listState = rememberLazyListState()
    var selectedRow by remember { mutableStateOf(0) }
    var buttonHeight by remember { mutableStateOf(0) }
    var listVisibleHeight by remember { mutableStateOf(0) }
    val pagerState = PagerState(currentPage = selectedRow)

    Row(
        modifier = Modifier
            .onGloballyPositioned {
                listVisibleHeight = it.size.height
            }
    ) {
        LazyColumn(
            state = listState,
            modifier = Modifier
                .wrapContentSize(),
            horizontalAlignment = Alignment.CenterHorizontally,
        ) {
            items(numberOfRows) { index ->
                Button(
                    modifier = Modifier
                        .wrapContentWidth()
                        .onGloballyPositioned {
                            buttonHeight = it.size.height
                        },
                    onClick = {}
                ) {
                    if (index == selectedRow) {
                        Text(text = index.toString(), color = Color.White)
                    } else {
                        Text(text = index.toString(), color = Color.Red)
                    }
                }
            }
        }

        LaunchedEffect(pagerState) {
            snapshotFlow { pagerState.currentPage }.collect { page ->
                if (page != selectedRow) {
                    //This doesn't work if selectedRow = page happens before the other code due to recomposition
                    //If it's after, it executes but it doesn't look right as it moves then highlights

                    selectedRow = page

                    listState.scrollToItem(0)
                    try {
                        listState.animateScrollBy(distanceToMiddleOfScreen(buttonHeight, listVisibleHeight, page))
                    } catch (exception: Exception) {
                        Log.e("scrollBug", "animateScrollBy Exception", exception)
                    }
                }
            }
        }

        HorizontalPager(
            count = 48,
            state = pagerState,
            modifier = Modifier
                .fillMaxWidth()
                .fillMaxHeight()
                .background(Color.White),
        ) { page ->
            Text(text = page.toString())
        }
    }
}

private fun distanceToMiddleOfScreen(itemHeight: Int, totalListHeight: Int, offset: Int): Float {
    return itemHeight.toFloat() * offset - (totalListHeight / 2) + itemHeight
}

您的寻呼机行为异常,因为您在到达重组时创建它的状态:

val pagerState = PagerState(currentPage = selectedRow)

这个初始化程序让您可以在自己的状态下使用,您有责任在重新组合之间记住它。

如果您在视图中使用它,要在重组之间保存它的状态,您需要使用记住版本:

val pagerState = rememberPagerState()

在此之后,您的 LaunchedEffect 将不会重新启动,也不会停止您的 animateScrollBy


在这个 feature request 关闭之前,我想不出滚动问题的一般解决方案。但是如果所有项目的大小都相同,则可以计算当前滚动位置、最终滚动位置等,这样就可以计算滚动的差异:

LaunchedEffect(Unit) {
    snapshotFlow { pagerState.currentPage }.collect { page ->
        if (page != selectedRow) {
            selectedRow = page

            val currentOffset = listState.firstVisibleItemIndex * buttonHeight + listState.firstVisibleItemScrollOffset
            val finalOffset = distanceToMiddleOfScreen(buttonHeight, listVisibleHeight, page)
            listState.animateScrollBy(finalOffset - currentOffset)
        }
    }
}

还有你的distanceToMiddleOfScreen不准确,可以这样修正:

return itemHeight * (offset + 0.5f) - totalListHeight / 2

结果: