【发布时间】:2021-06-04 16:44:57
【问题描述】:
我想在 LazyColumn 中以编程方式动态启用和禁用滚动。
LazyListState 上似乎没有任何相关功能或LazyColumn 本身上的相关参数。如何在 Compose 中实现这一点?
【问题讨论】:
标签: android kotlin android-jetpack-compose
我想在 LazyColumn 中以编程方式动态启用和禁用滚动。
LazyListState 上似乎没有任何相关功能或LazyColumn 本身上的相关参数。如何在 Compose 中实现这一点?
【问题讨论】:
标签: android kotlin android-jetpack-compose
(目前)没有内置的方法可以做到这一点,这是一个合理的功能要求。
但是,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 阻止了所有输入和阻止?在伴奏寻呼机中询问的原因使用LazyListState 和PagerState 作为ScrollState 代表 - 我想禁用用户滚动,同时允许子页面和可组合项仍然使用事件,无论如何这可能只是事件的传递,只是不考虑滚动事件(寻呼机 api 目前不灵活且有限)?
由于1.2.0-alpha01userScrollEnabledwas added到LazyColumn、LazyRow和LazyVerticalGrid
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 似乎效果更好 - “从后代到祖先的树。”。当应用于上述代码时,这意味着来自子级的所有未使用事件都被此修饰符使用,允许后代交互和禁用祖先——这可能是此功能请求可配置的最佳选择。这也适用于伴奏库中的Pager和PagerState。
PointerEventPass.Main,儿童捏缩放仍然被禁用-我确实阅读/查看了文档中的状态图以获取答案,但它并没有真正帮助提供解决方案。
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
)
){
...
但是,这确实会阻止程序化滚动。
【讨论】: