android - 使用底部片材支架中的底部片材调整内容高度

标签 android kotlin android-jetpack-compose bottom-sheet compose-recomposition

我想动态更改底部片材折叠中相对于底部片材的内容高度,如下所示:

bottom sheet collapsed bottom sheet expanded

这是我到目前为止所尝试过的: `


val bottomSheetState = rememberBottomSheetState(initialValue =BottomSheetValue.Expanded )

val bottomSheetScaffoldState= rememberBottomSheetScaffoldState(
        bottomSheetState = bottomSheetState)

val heightPixels= LocalContext.current.resources.displayMetrics.heightPixels

BottomSheetScaffold(
modifier = Modifier.fillMaxSize()
sheetPeekHeight = 68.dp,
scaffoldState = bottomSheetScaffoldState,
sheetContent = {
    /*
    sheet content
     */
}) {
    Box(modifier = Modifier
        .fillMaxWidth()
        .fillMaxHeight(((bottomSheetState.offset.value)
                / (heightPixels)).let { if (it == 0f) 1f else it })
    ) {
        /*
        content
         */
    }
}

但它依赖于系统高度(以像素为单位),并且每次 Bottom Sheet 的高度发生变化时,其内容的框都会重新组合

最佳答案

您可以使用 Modifier.onPlaced/onSizeChanged/onGloballyPositionedBoxWithConstaintsBottomSheetScaffold 的总高度获取内容高度并获取高度与

动态内容高度示例

enter image description here

@OptIn(ExperimentalMaterialApi::class)
@Preview
@Composable
private fun BottomSheetScaffoldHeightSample() {

    val scaffoldState = rememberBottomSheetScaffoldState()

    var sheetPeekHeight by remember {
        mutableStateOf(0.dp)
    }

    val density = LocalDensity.current

    BoxWithConstraints {

        val parentHeight = maxHeight

        SideEffect {
            println("Parent is recomposing...")
        }

        BottomSheetScaffold(
            modifier = Modifier.fillMaxSize().drawWithContent {
                if (sheetPeekHeight != 0.dp) {
                    drawContent()
                }
            },
            sheetPeekHeight = sheetPeekHeight,
            scaffoldState = scaffoldState,
            drawerShape = RoundedCornerShape(16.dp),
            sheetContent = {

                SideEffect {
                    println("SheetContent. recomposing...")
                }

                Box(
                    modifier = Modifier
                        .onPlaced {
                            if (sheetPeekHeight == 0.dp) {
                                sheetPeekHeight = with(density) {
                                    it.size.height.toDp()
                                }
                            }
                        }
                        .fillMaxWidth()
                        .height(200.dp)
                        .background(Color.Green)
                ) {
                    Text(
                        text = "sheetContent",
                        color = Color.White,
                        fontSize = 60.sp
                    )
                }
            },
            content = {

                SideEffect {
                    println("Content recomposing...")
                }
                Box(
                    modifier = Modifier
                        .fillMaxWidth()
                        .height(maxHeight - sheetPeekHeight)
                        .background(Purple400),
                    contentAlignment = Alignment.Center
                ) {
                    Text(
                        text = "Content",
                        color = Color.White,
                        fontSize = 60.sp
                    )
                }
            }
        )
    }
}

您也可以在此处使用下面的 subComposeLayout 方法,您只需添加sheetContent作为主要内容

您还可以根据内容设置工作表高度,如下所示

动态纸张高度示例

enter image description here

@OptIn(ExperimentalMaterialApi::class)
@Preview
@Composable
private fun BottomSheetScaffoldHeightSample() {

    val scaffoldState = rememberBottomSheetScaffoldState()

    var sheetPeekHeight by remember {
        mutableStateOf(0.dp)
    }

    val density = LocalDensity.current

    BoxWithConstraints {

        val parentHeight = maxHeight

        BottomSheetScaffold(
            modifier = Modifier.fillMaxSize().drawWithContent {
                if (sheetPeekHeight != 0.dp) {
                    drawContent()
                }
            },
            sheetPeekHeight = sheetPeekHeight,
            scaffoldState = scaffoldState,
            drawerShape = RoundedCornerShape(16.dp),
            sheetContent = {

                SideEffect {
                    println("SheetContent. recomposing...")
                }

                LazyColumn(
                    modifier = Modifier.fillMaxSize(),
                    verticalArrangement = Arrangement.spacedBy(16.dp)
                ) {
                    items(100) {
                        Text(
                            text = "Row $it",
                            fontSize = 20.sp,
                            modifier = Modifier
                                .shadow(2.dp)
                                .background(Color.White)
                                .fillMaxWidth()
                                .padding(16.dp)
                        )
                    }
                }
            },
            content = {

                SideEffect {
                    println("Content recomposing...")
                }
                Box(
                    modifier = Modifier.fillMaxWidth()
                        .onPlaced {
                            if (sheetPeekHeight == 0.dp) {
                                sheetPeekHeight = with(density) {
                                    parentHeight - it.size.height.toDp()
                                }
                            }
                        }
                        .background(Purple400)
                        .padding(48.dp),
                    contentAlignment = Alignment.Center
                ) {
                    Text(
                        text = "Content",
                        color = Color.White,
                        fontSize = 60.sp
                    )
                }
            }
        )
    }
}

但是,这将安排在 BoxWithConstaints 范围或任何其他范围和内容中进行重组,因为我们使用 MutableState 更改高度,以免遇到可以使用的内容高度跳跃

Modifier.drawContent 如上例所示。

其他选项是使用 SubcomposeLayout (如本答案所示)来获取准确的高度,而无需父级重组。

https://stackoverflow.com/a/73357119/5457853

@Composable
fun DimensionSubcomposeLayout(
    modifier: Modifier = Modifier,
    mainContent: @Composable () -> Unit,
    dependentContent: @Composable (Dp) -> Unit
) {

    SubcomposeLayout(
        modifier = modifier
    ) { constraints: Constraints ->

        // Subcompose(compose only a section) main content and get Placeable
        val mainPlaceables: List<Placeable> = subcompose(SlotsEnum.Main, mainContent)
            .map {
                it.measure(constraints.copy(minWidth = 0, minHeight = 0))
            }

        var maxWidth = 0
        var maxHeight = 0

        mainPlaceables.forEach { placeable: Placeable ->
            maxWidth += placeable.width
            maxHeight = placeable.height
        }

        val dependentPlaceables: List<Placeable> = subcompose(SlotsEnum.Dependent) {
            dependentContent((constraints.maxHeight-maxHeight).toDp())
        }
            .map { measurable: Measurable ->
                measurable.measure(constraints)
            }

        // This are for Modifier.fillMaxSize if you wish to use with other modifiers
        // you need check Constraints bounds and vertical scroll
        layout(constraints.maxWidth, constraints.maxHeight) {
            dependentPlaceables.forEach { placeable: Placeable ->
                placeable.placeRelative(0, 0)
            }
        }
    }
}

enum class SlotsEnum { Main, Dependent }

并将其用作

@OptIn(ExperimentalMaterialApi::class)
@Preview
@Composable
private fun BottomSheetScaffoldHeightSample2() {

    val scaffoldState = rememberBottomSheetScaffoldState()

    val content = @Composable {
        Box(
            modifier = Modifier.fillMaxWidth()
                .background(Purple400)
                .padding(48.dp),
            contentAlignment = Alignment.Center
        ) {

            SideEffect {
                println("Content recomposing...")
            }

            Text(
                text = "Content",
                color = Color.White,
                fontSize = 60.sp
            )
        }
    }

    DimensionSubcomposeLayout(
        modifier = Modifier.fillMaxSize(),
        mainContent = content
    ) { sheetPeekHeight: Dp ->

        SideEffect {
            println("Parent is recomposing...")
        }

        BottomSheetScaffold(
            modifier = Modifier.fillMaxSize(),
            sheetPeekHeight = sheetPeekHeight,
            scaffoldState = scaffoldState,
            drawerShape = RoundedCornerShape(16.dp),
            sheetContent = {

                SideEffect {
                    println("SheetContent. recomposing...")
                }

                LazyColumn(
                    modifier = Modifier.fillMaxSize(),
                    verticalArrangement = Arrangement.spacedBy(16.dp)
                ) {
                    items(100) {
                        Text(
                            text = "Row $it",
                            fontSize = 20.sp,
                            modifier = Modifier
                                .shadow(2.dp)
                                .background(Color.White)
                                .fillMaxWidth()
                                .padding(16.dp)
                        )
                    }
                }
            },
            content = {
                content()
            }
        )
    }
}

关于android - 使用底部片材支架中的底部片材调整内容高度,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/74412989/

相关文章:

android - 我们如何获得 Composable 在屏幕中的位置/大小?

android - Intellij IDEA Android 预览不起作用

android - 如何知道 Android NDK 中访问文件的确切位置

android - java.lang.NoSuchMethodError in external java dependency inside the java dependency from Kotlin on Android 5

Android jetpack compose NoSuchMethodError : No static method setContent

kotlin - 如何在 jetpack Compose 中将输入类型设置为整数?

Android普通按钮变透明

android - 无法在多设备上同时运行 Monkeyrunner 脚本(例如两个 Monkeyrunner 进程)

android - 尝试分离/关闭 Firestore 快照时,条件 'listenerReg != null' 始终为 'true' (Android)

kotlin - 在 Kotlin 中,重音是什么/意味着什么?