在BottomSheetDialogFragment中使用LazyColumn出现滚动问题

13
我在BottomSheetDialogFragment中使用了LazyColumn,但是如果向上滚动LazyColumn列表,则BottomSheet对话框会滚动而不是LazyColumn列表。似乎BottomSheetDialogFragment截取了用户的触摸输入。

效果如下图所示:

如何在BottomSheetDialogFragment内正确使用LazyColumn

MyBottomSheetDialogFragment.kt:

class MyBottomSheetDialogFragment : BottomSheetDialogFragment() {
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        return ComposeView(requireContext()).apply {
            setContent {
                Column(horizontalAlignment = Alignment.CenterHorizontally) {
                    Text("Header", color = Color.Black)
                    LazyColumn(
                        Modifier
                            .weight(1f)
                            .fillMaxWidth()) {
                        items(100) {
                            Text("Item $it", Modifier.fillMaxWidth(), Color.Black)
                        }
                    }
                }
            }
        }
    }
}

并使用以下代码进行显示:

MyBottomSheetDialogFragment().show(activity.supportFragmentManager, null)

当我们使用XML RecyclerView列表时,为了解决这个问题,我们不得不像这里所描述的那样NestedScrollView包装RecyclerView列表。但是如何在Jetpack Compose中解决这个问题呢?

4个回答

18
自从 1.2.0-beta01 版本以来,可以通过使用 rememberNestedScrollInteropConnection 来解决问题。
Modifier.nestedScroll(rememberNestedScrollInteropConnection())

在我的情况下,BottomSheetDialogFragment 是标准的 View,并且它具有ID为 containerComposeView。在 onViewCreated 中,我进行了以下操作:
binding.container.setContent {
    AppTheme {
        Surface(
            modifier = Modifier.nestedScroll(rememberNestedScrollInteropConnection())
        ) {
            LazyColumn {
                // ITEMS
            }
        }
    }
}

现在列表已经以正确的方式滚动。


7
当我尝试向下拖动以关闭底部表单时,底部表单的顶部无法响应。这是预期行为吗?我的LazyColumn上面有一个Text-composable. - Tob237
1
@Tob237 你解决了向下拖动的问题吗?我也遇到了同样的情况,即表格顶部不响应拖动事件。 - Marcel Schnelle
我和@MarcelSchnelle一样遇到了同样的问题。在这里询问了它https://dev59.com/ZdT7oIgBc1ULPQZF8pgM - Joao
1
对我来说,顶部拖动问题可以通过在标题/顶部组合中添加 modifier = Modifier.verticalScroll(rememberScrollState()) 来解决。文档还提到:“为了解决这个问题,请确保将可滚动修饰符设置为这些类型的嵌套组合”。 - Edward van Raak
@EdwardvanRaak的修复方案有助于解决此问题,因为“不滚动”元素的滚动增量也已传递给rememberNestedScrollInteropConnection提供的NestedScrollConnection。您可能会遇到渲染错误“垂直可滚动组件的最大高度被测量为无穷大...”。为了解决这个问题,您可以通过使用“Modifier.height(intrinsicSize = IntrinsicSize.Max)”为那些“父级”元素添加高度。 - undefined

3
您可以尝试使用这个方案

这是一个解决方案,用于解决在Compose中的嵌套滚动布局无法作为视图系统中的嵌套滚动子项的问题(https://issuetracker.google.com/issues/174348612)。

在您的情况下的示例用法:

class MyBottomSheetDialogFragment : BottomSheetDialogFragment() {
    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        return ComposeView(requireContext()).apply {
            setContent {
                Surface(
                    modifier = Modifier.nestedScroll(rememberViewInteropNestedScrollConnection())
                ){
                    LazyColumn(
                        Modifier
                            .weight(1f)
                            .fillMaxWidth()) {
                        items(100) {
                            Text("Item $it", Modifier.fillMaxWidth(), Color.Black)
                        }
                    }
                }
            }
        }
    }
}

1

我想使用 ModalBottomSheetLayout,但是目前应用程序的架构不允许轻松地将其实现到所需的位置。似乎需要进行大量的重构。这就是为什么我使用 BottomSheetDialogFragment 的原因。此外,在应用程序中,我们决定使用这项新技术编写新屏幕,即 Jetpack Compose。无论如何,谢谢你的回答! - Mike

1
我找到了一个很好的解决方案。我们可以使用Kotlin扩展来在Android视图上使用Jetpack Compose底部表单。
更多关于其工作原理的细节请参见这里
以下是我们需要的所有代码:
// Extension for Activity
fun Activity.showAsBottomSheet(content: @Composable (() -> Unit) -> Unit) {
    val viewGroup = this.findViewById(android.R.id.content) as ViewGroup
    addContentToView(viewGroup, content)
}

// Extension for Fragment
fun Fragment.showAsBottomSheet(content: @Composable (() -> Unit) -> Unit) {
    val viewGroup = requireActivity().findViewById(android.R.id.content) as ViewGroup
    addContentToView(viewGroup, content)
}

// Helper method
private fun addContentToView(
    viewGroup: ViewGroup,
    content: @Composable (() -> Unit) -> Unit
) {
    viewGroup.addView(
        ComposeView(viewGroup.context).apply {
            setContent {
                BottomSheetWrapper(viewGroup, this, content)
            }
        }
    )
}

@OptIn(ExperimentalMaterialApi::class)
@Composable
private fun BottomSheetWrapper(
    parent: ViewGroup,
    composeView: ComposeView,
    content: @Composable (() -> Unit) -> Unit
) {
    val TAG = parent::class.java.simpleName
    val coroutineScope = rememberCoroutineScope()
    val modalBottomSheetState = rememberModalBottomSheetState(ModalBottomSheetValue.Hidden)
    var isSheetOpened by remember { mutableStateOf(false) }

    ModalBottomSheetLayout(
        sheetBackgroundColor = Color.Transparent,
        sheetState = modalBottomSheetState,
        sheetContent = {
            content {
                // Action passed for clicking close button in the content
                coroutineScope.launch {
                    modalBottomSheetState.hide() // will trigger the LaunchedEffect
                }
            }
        }
    ) {}

    BackHandler {
        coroutineScope.launch {
            modalBottomSheetState.hide() // will trigger the LaunchedEffect
        }
    }

    // Take action based on hidden state
    LaunchedEffect(modalBottomSheetState.currentValue) {
        when (modalBottomSheetState.currentValue) {
            ModalBottomSheetValue.Hidden -> {
                when {
                    isSheetOpened -> parent.removeView(composeView)
                    else -> {
                        isSheetOpened = true
                        modalBottomSheetState.show()
                    }
                }
            }
            else -> {
                Log.i(TAG, "Bottom sheet ${modalBottomSheetState.currentValue} state")
            }
        }
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接