【问题标题】:How to disable and enable scrolling in LazyColumn/LazyRow in Jetpack Compose?如何在 Jetpack Compose 的 LazyColumn/LazyRow 中禁用和启用滚动?
【发布时间】:2021-06-04 16:44:57
【问题描述】:

我想在 LazyColumn 中以编程方式动态启用和禁用滚动。

LazyListState 上似乎没有任何相关功能或LazyColumn 本身上的相关参数。如何在 Compose 中实现这一点?

【问题讨论】:

    标签: android kotlin android-jetpack-compose


    【解决方案1】:

    (目前)没有内置的方法可以做到这一点,这是一个合理的功能要求。

    但是,scroll API 足够灵活,我们可以自己添加它。基本上,我们在MutatePriority.PreventUserInput 创建一个永无止境的假滚动来防止滚动,然后使用相同优先级的无操作滚动来取消第一个“滚动”并重新启用滚动。

    下面是 LazyListState 上的两个实用函数,用于禁用/重新启用滚动,以及它们的实际演示(需要一些导入,但 Android Studio 应该会为您推荐)。

    请注意,因为我们正在控制滚动来执行此操作,所以调用 reenableScrolling 还将取消任何正在进行的滚动或甩动(也就是说,您应该只在禁用滚动并且想要重新启用它时调用它,而不仅仅是确认它已启用)。

    fun LazyListState.disableScrolling(scope: CoroutineScope) {
        scope.launch {
            scroll(scrollPriority = MutatePriority.PreventUserInput) {
                // Await indefinitely, blocking scrolls
                awaitCancellation()
            }
        }
    }
    
    fun LazyListState.reenableScrolling(scope: CoroutineScope) {
        scope.launch {
            scroll(scrollPriority = MutatePriority.PreventUserInput) {
                // Do nothing, just cancel the previous indefinite "scroll"
            }
        }
    }
    
    @Composable
    fun StopScrollDemo() {
        val scope = rememberCoroutineScope()
        val state = rememberLazyListState()
        Column {
            Row {
                Button(onClick = { state.disableScrolling(scope) }) { Text("Disable") }
                Button(onClick = { state.reenableScrolling(scope) }) { Text("Re-enable") }
            }
            LazyColumn(Modifier.fillMaxWidth(), state = state) {
                items((1..100).toList()) {
                    Text("$it", fontSize = 24.sp)
                }
            }
        }
    }
    

    【讨论】:

    • 看起来这会阻止所有与子可组合项的交互,包括文本字段、下拉菜单等。假设这是因为MutatePriority.PreventUserInput 阻止了所有输入和阻止?在伴奏寻呼机中询问的原因使用LazyListStatePagerState 作为ScrollState 代表 - 我想禁用用户滚动,同时允许子页面和可组合项仍然使用事件,无论如何这可能只是事件的传递,只是不考虑滚动事件(寻呼机 api 目前不灵活且有限)?
    【解决方案2】:

    由于1.2.0-alpha01userScrollEnabledwas addedLazyColumnLazyRowLazyVerticalGrid


    1.1.0 及更早版本的答案:

    @Ryan 的解决方案还将禁用以编程方式调用的滚动。

    这是维护者在this feature request 中提出的解决方案。它将禁用滚动,允许程序化滚动以及子视图触摸。

    内置解决方案已经是done,应该会在即将发布的版本中提供。

    private val VerticalScrollConsumer = object : NestedScrollConnection {
        override fun onPreScroll(available: Offset, source: NestedScrollSource) = available.copy(x = 0f)
        override suspend fun onPreFling(available: Velocity) = available.copy(x = 0f)
    }
    
    private val HorizontalScrollConsumer = object : NestedScrollConnection {
        override fun onPreScroll(available: Offset, source: NestedScrollSource) = available.copy(y = 0f)
        override suspend fun onPreFling(available: Velocity) = available.copy(y = 0f)
    }
    
    fun Modifier.disabledVerticalPointerInputScroll(disabled: Boolean = true) =
        if (disabled) this.nestedScroll(VerticalScrollConsumer) else this
    
    fun Modifier.disabledHorizontalPointerInputScroll(disabled: Boolean = true) =
        if (disabled) this.nestedScroll(HorizontalScrollConsumer) else this
    

    用法:

    LazyColumn(
        modifier = Modifier.disabledVerticalPointerInputScroll()
    ) {
        // ...
    }
    

    【讨论】:

    • 目前这会禁用一切,所以子可组合项不会收到任何事件?无论如何,是否允许子可组合物仍然消耗事件,所以这个修饰符是一个简单的传递?
    • 好的,找到了答案 - 阅读文档 PointerEventPass.Main 似乎效果更好 - “从后代到祖先的树。”。当应用于上述代码时,这意味着来自子级的所有未使用事件都被此修饰符使用,允许后代交互和禁用祖先——这可能是此功能请求可配置的最佳选择。这也适用于伴奏库中的PagerPagerState
    • @MarkKeen 我对您的解决方案进行了更深入的测试,并且使用 `PointerEventPass.Main' 进行了一些操作,偶尔我仍然可以滚动列表。
    • 似乎缺乏以一致和可靠的方式控制手势/触摸系统将是一个问题——要么我没有正确理解文档,要么它有点错误。我还发现即使使用PointerEventPass.Main,儿童捏缩放仍然被禁用-我确实阅读/查看了文档中的状态图以获取答案,但它并没有真正帮助提供解决方案。
    【解决方案3】:

    NestedScrollConnection 允许您使用任何应用于惰性列或行的滚动。当为 true 时,所有可用的滚动都被消耗掉。如果为 false,则不消耗任何内容,并且滚动正常进行。有了这些信息,您可以了解如何通过返回某个因子的偏移倍数来扩展慢速/快速滚动。

    fun Modifier.scrollEnabled(
        enabled: Boolean,
    ) = nestedScroll(
        connection = object : NestedScrollConnection {
            override fun onPreScroll(
                available: Offset,
                source: NestedScrollSource
            ): Offset = if(enabled) Offset.Zero else available
        }
    )
    

    可以这样使用:

    LazyColumn(
        modifier = Modifier.scrollEnabled(
            enabled = enabled, //provide a mutable state boolean here
        )
    ){
        ...
    

    但是,这确实会阻止程序化滚动。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-09-26
      • 1970-01-01
      • 2021-08-13
      • 2021-09-28
      • 1970-01-01
      • 2022-01-15
      • 1970-01-01
      • 2021-11-26
      相关资源
      最近更新 更多