如何检测用户是否向上/向下滚动 LazyColumn - Jetpack Compose

and*_*321 1 scroll kotlin android-jetpack-compose android-jetpack-compose-lazy-column

如何检测用户何时在 LazyColumn 中向上或向下滚动?我试图在用户向下滚动时隐藏屏幕上的元素,并在用户开始向上滚动时再次显示它。

Thr*_*ian 9

lazyListState.firstVisibleItemIndex这可以通过将和与它们之前的值进行比较来完成lazyListState.firstVisibleItemScrollOffset

在此输入图像描述

您可以像 LazyListState 一样封装此逻辑

class DirectionalLazyListState(
    private val lazyListState: LazyListState
) {
    private var positionY = lazyListState.firstVisibleItemScrollOffset
    private var visibleItem = lazyListState.firstVisibleItemIndex


    val scrollDirection by derivedStateOf {
        if (lazyListState.isScrollInProgress.not()) {
            ScrollDirection.None
        } else {
            val firstVisibleItemIndex = lazyListState.firstVisibleItemIndex
            val firstVisibleItemScrollOffset =
                lazyListState.firstVisibleItemScrollOffset

            // We are scrolling while first visible item hasn't changed yet
            if (firstVisibleItemIndex == visibleItem) {
                val direction = if (firstVisibleItemScrollOffset > positionY) {
                    ScrollDirection.Down
                } else {
                    ScrollDirection.Up
                }
                positionY = firstVisibleItemScrollOffset

                direction
            } else {

                val direction = if (firstVisibleItemIndex > visibleItem) {
                    ScrollDirection.Down
                } else {
                    ScrollDirection.Up
                }
                positionY = firstVisibleItemScrollOffset
                visibleItem = firstVisibleItemIndex
                direction
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

通过记住调用构造函数

@Composable
fun rememberDirectionalLazyListState(
    lazyListState: LazyListState,
): DirectionalLazyListState {
    return remember {
        DirectionalLazyListState(lazyListState)
    }
}
Run Code Online (Sandbox Code Playgroud)

并使用它

val lazyListState = rememberLazyListState()
val directionalLazyListState = rememberDirectionalLazyListState(
    lazyListState
)
Run Code Online (Sandbox Code Playgroud)

枚举类

enum class ScrollDirection {
    Up, Down, None
}
Run Code Online (Sandbox Code Playgroud)

完整演示

@Preview
@Composable
private fun ScrollDirectionSample() {

    val lazyListState = rememberLazyListState()
    val directionalLazyListState = rememberDirectionalLazyListState(
        lazyListState
    )

    val text by remember {
        derivedStateOf {
            "isScrollInProgress: ${lazyListState.isScrollInProgress}\n" +
                    "firstVisibleItemIndex: ${lazyListState.firstVisibleItemIndex}\n" +
                    "firstVisibleItemScrollOffset: ${lazyListState.firstVisibleItemScrollOffset}"
        }
    }


    val color = when (directionalLazyListState.scrollDirection) {
        ScrollDirection.Up -> Color.Green
        ScrollDirection.Down -> Color.Blue
        else -> Color.Black
    }

    Column {

        Text(text, fontSize = 16.sp)
        Text(
            "Direction: ${directionalLazyListState.scrollDirection}",
            fontSize = 24.sp,
            color = color,
            fontWeight = FontWeight.Bold
        )
        LazyColumn(
            state = lazyListState,
            modifier = Modifier.fillMaxSize()
        ) {
            items(50) {
                Text(
                    text = "Row $it",
                    fontSize = 22.sp,
                    color = Color.White,
                    modifier = Modifier
                        .fillMaxWidth()
                        .background(Color.Red)
                        .padding(8.dp)
                )
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

上面的代码无法确定手指按下时是移动还是向下还是空闲,因为按下时无论指针是否未移动isScrollInProgress都会返回true 。如果用户在按下指针时不移动指针或isScrollInProgress为 true,则可以添加超时设置为“无”。

@Stable
class DirectionalLazyListState(
    private val lazyListState: LazyListState,
    private val coroutineScope: CoroutineScope
)  {
    private var positionY = lazyListState.firstVisibleItemScrollOffset
    private var visibleItem = lazyListState.firstVisibleItemIndex

    private var currentTime = System.currentTimeMillis()
    var scrollDirection by mutableStateOf(ScrollDirection.None)

    init {

        coroutineScope.launch {
            while (isActive) {
                delay(120)
                if (System.currentTimeMillis() - currentTime > 120) {
                    scrollDirection = ScrollDirection.None
                }
            }
        }

        snapshotFlow {
            val scrollInt = if (lazyListState.isScrollInProgress) 20000 else 10000
            val visibleItemInt = lazyListState.firstVisibleItemIndex * 10
            scrollInt + visibleItemInt + lazyListState.firstVisibleItemScrollOffset
        }
            .onEach {
                if (lazyListState.isScrollInProgress.not()) {
                    scrollDirection = ScrollDirection.None
                } else {

                    currentTime = System.currentTimeMillis()

                    val firstVisibleItemIndex = lazyListState.firstVisibleItemIndex
                    val firstVisibleItemScrollOffset =
                        lazyListState.firstVisibleItemScrollOffset

                    // We are scrolling while first visible item hasn't changed yet
                    if (firstVisibleItemIndex == visibleItem) {
                        val direction = if (firstVisibleItemScrollOffset > positionY) {
                            ScrollDirection.Down
                        } else {
                            ScrollDirection.Up
                        }
                        positionY = firstVisibleItemScrollOffset

                        scrollDirection = direction
                    } else {

                        val direction = if (firstVisibleItemIndex > visibleItem) {
                            ScrollDirection.Down
                        } else {
                            ScrollDirection.Up
                        }
                        positionY = firstVisibleItemScrollOffset
                        visibleItem = firstVisibleItemIndex
                        scrollDirection = direction
                    }
                }
            }
            .launchIn(coroutineScope)
    }


//    val scrollDirection by derivedStateOf {
//        if (lazyListState.isScrollInProgress.not()) {
//            ScrollDirection.None
//        } else {
//            val firstVisibleItemIndex = lazyListState.firstVisibleItemIndex
//            val firstVisibleItemScrollOffset =
//                lazyListState.firstVisibleItemScrollOffset
//
//            // We are scrolling while first visible item hasn't changed yet
//            if (firstVisibleItemIndex == visibleItem) {
//                val direction = if (firstVisibleItemScrollOffset > positionY) {
//                    ScrollDirection.Down
//                } else {
//                    ScrollDirection.Up
//                }
//                positionY = firstVisibleItemScrollOffset
//
//                direction
//            } else {
//
//                val direction = if (firstVisibleItemIndex > visibleItem) {
//                    ScrollDirection.Down
//                } else {
//                    ScrollDirection.Up
//                }
//                positionY = firstVisibleItemScrollOffset
//                visibleItem = firstVisibleItemIndex
//                direction
//            }
//        }
//    }
}
Run Code Online (Sandbox Code Playgroud)