【问题标题】:How to prevent Jetpack Compose ExposedDropdownMenuBox from showing menu when scrolling如何防止 Jetpack Compose ExposedDropdownMenu Box 在滚动时显示菜单
【发布时间】:2021-12-26 09:17:00
【问题描述】:

我正在尝试使用 Jetpack Compose 的 ExposedDropdownMenuBox,但我无法阻止它在滚动时显示菜单。

例如,下面是复制此问题的代码:

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            MyApplicationTheme {
                Surface(color = MaterialTheme.colors.background) {
                    Column(
                        modifier = Modifier
                            .padding(horizontal = 8.dp)
                            .verticalScroll(rememberScrollState()),
                        verticalArrangement = Arrangement.spacedBy(8.dp)
                    ) {
                        repeat(20){
                            ExposedDropdownMenuSample()
                        }
                    }
                }
            }
        }
    }
}

ExposedDropdownMenuSample取自官方样本。

This is a GIF showing the problem.

如何防止这种情况发生?

此代码使用的是撰写版本1.1.0-rc01

【问题讨论】:

  • 我没有看到你在这里做错了什么,我建议你 report 给问题跟踪器。在它修复之前,您可以查看this solution
  • 谢谢@PhilipDukhov。我会报告并使用您的解决方案。
  • 不客气。如果它对你有帮助,请给它投票 =)
  • 我不能投票...我觉得太新了?
  • 是的,我忘记了 =) 你需要 15 个评分。

标签: android kotlin android-jetpack-compose


【解决方案1】:

我能够用这个丑陋的黑客解决这个问题:

private fun Modifier.cancelOnExpandedChangeIfScrolling() = pointerInput(Unit) {
    forEachGesture {
        coroutineScope {
            awaitPointerEventScope {
                var event: PointerEvent
                var startPosition = Offset.Unspecified
                var cancel = false
                
                do {
                    event = awaitPointerEvent(PointerEventPass.Initial)
                    if (startPosition == Offset.Unspecified) {
                        startPosition = event.changes.first().position
                    }

                    val distance = startPosition.minus(event.changes.last().position).getDistance()
                    cancel = distance > 10f || cancel
                } while (
                    !event.changes.all { it.changedToUp() }
                )

                if (cancel) {
                    event.changes.forEach { it.consumeAllChanges() }
                }
            }
        }
    }
}

然后将其添加到ExposedDropdownMenuBox like:

ExposedDropdownMenuBox(
    expanded = expanded,
    onExpandedChange = {
        expanded = !expanded
    }, modifier = Modifier.cancelOnExpandedChangeIfScrolling()
)

所以它基本上检查是否有超过 10 像素的拖动? 如果为真,则消耗事件,因此它不会触发 onExpandedChange

10 只是一个神奇的数字,对我的测试很有效,但对于高分辨率屏幕设备来说似乎太低了。我确信有更好的方法来计算这个数字......也许更有经验的人可以帮助解决这个问题,直到我们找到适当的解决方案?

【讨论】:

    【解决方案2】:

    我发现了一个稍微不那么老套的解决方法:

    您可以从 Column 的滚动状态获得滚动是否正在进行的信息。

    val scrollState = rememberScrollState()
    val isScrolling = scrollState.isScrollInProgress
    
    Column(
      modifier = Modifier
        .padding(horizontal = 8.dp)
        .verticalScroll(scrollState),
        ...
    ) ...
    
    

    ExposedDropdownMenuBox中,您可以将监听器更改为

    onExpandedChange = {
      expanded = !expanded && !isScrolling
    },
    

    => 滚动时永远不会打开下拉菜单。一旦您开始在主列中滚动,它也会自动关闭。但是,可以在下拉列表中滚动。 当然你也可以把它改成类似

    expanded = if (isScrolling) expanded else !expanded
    

    滚动时保持原样

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-05-26
      • 1970-01-01
      • 2011-04-10
      • 2023-02-14
      • 2022-11-21
      • 1970-01-01
      • 1970-01-01
      • 2022-08-18
      相关资源
      最近更新 更多