android 如何检查组合是否为空?

rekjcdws  于 2023-10-14  发布在  Android
关注(0)|答案(1)|浏览(140)

我正在构建一个可组合的,比如:

@Composable
fun BottomSectionWrapper(
    content: @Composable () -> Unit,
    bottomSection: @Composable () -> Unit,
)

现在我想知道参数bottomSection是否为空,以便相应地调整布局。这可能吗?
我不会再进一步定义“空”了;我会采取技术上可行的方法

  • 一个空的lambda {}
  • 一个不会发出composable的lambda:{ if(false)Text(“Test”)}
  • 一个大小为零的lambda:{ Box(Modifier.size(0.dp))}

让我解释一下我的典型用例:我使用这个 Package 器将按钮附加到屏幕的下端。如果有一个按钮,我想添加一个淡入淡出。
我试过什么?
我试着把lambda设置为可空的--但编译不了

BottomSectionWrapper(
    content = { /* ... */ },
    bottomSection = if(showDeleteButton){
        Button { Text("Delete") }
    } else null
)

另外:直觉上,我会这样写(这不会被简单的null检查检测到):

BottomSectionWrapper(
    content = { /* ... */ },
    bottomSection = {
        if (showDeleteButton) {
            Button { Text("Delete") }
        }
    }
)
t5zmwmid

t5zmwmid1#

如果你想拥有可空的内容,你需要将声明设置为

@Composable
fun BottomSectionWrapper(
    modifier: Modifier = Modifier,
    content: @Composable (() -> Unit)? = null,
    bottomSection: @Composable (() -> Unit)? = null,
) {
    Column(modifier = modifier) {
        content?.invoke()
        bottomSection?.invoke()
    }
}

你可以像这个演示一样使用它

@Preview
@Composable
private fun BottomSectionWrapperTest() {

    Column {
        var isSet by remember {
            mutableStateOf(false)
        }

        Button(onClick = { isSet = isSet.not() }) {
            Text("isSet: $isSet")
        }
        

        val bottomSection: (@Composable () -> Unit)? = if (isSet) {
            {
                Box(modifier = Modifier.size(100.dp).background(Color.Green)) {
                    Text("Bottom Section")
                }
            }

        } else null

        BottomSectionWrapper(
            modifier = Modifier.border(2.dp, Color.Red),
            content = {
                Text("Content Section")
            },
            bottomSection = bottomSection
        )
    }
}

如果您希望检测Composable是否为空或具有0.dp大小或任何大小,您可以使用Layout与不同的方法,尺寸设置和放置选项。
第二个带有Modifier.layoutId的字符串用于默认的可组合字符串,如TextFieldTab

@Composable
fun BottomSectionWrapperLayout(
    modifier: Modifier = Modifier,
    content: @Composable () -> Unit = {},
    bottomSection: @Composable () -> Unit = {},
) {

    Layout(
        modifier = modifier,
        contents = listOf(content, bottomSection)
    ) { measurables: List<List<Measurable>>, constraints: Constraints ->

        val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0)

        val contentPlaceable: Placeable? =
            measurables.first().firstOrNull()?.measure(looseConstraints)

        val bottomSectionPlaceable: Placeable? =
            measurables.last().firstOrNull()?.measure(looseConstraints)

        println("content: $contentPlaceable, bottomSectionPlaceable: $bottomSectionPlaceable")

        // you can set width and height as required, i make it as a Column
        // this width and height determines whether it's a Column, Row or a Box

        val contentWidth = contentPlaceable?.width ?: 0
        val contentHeight = contentPlaceable?.height ?: 0

        val bottomSectionWidth = bottomSectionPlaceable?.width ?: 0
        val bottomSectionHeight = bottomSectionPlaceable?.height ?: 0

        val totalWidth = if (constraints.hasFixedWidth && constraints.hasBoundedWidth)
            constraints.maxWidth else contentWidth.coerceAtLeast(bottomSectionWidth)
            .coerceIn(constraints.minWidth, constraints.maxWidth)

        val totalHeight = if (constraints.hasFixedHeight && constraints.hasBoundedHeight)
            constraints.maxHeight else (contentHeight + bottomSectionHeight)
            .coerceIn(constraints.minHeight, constraints.maxHeight)

        layout(totalWidth, totalHeight) {
            // You can place them according to your logic here
            contentPlaceable?.placeRelative(0, 0)
            bottomSectionPlaceable?.placeRelative(0, contentHeight)
        }
    }
}

使用这个Layout,你可以通过检查Placeable是否为null来检测它们是否为空,这意味着传递了空的lambda,如果不是null,你可以检查它的大小。

@Preview
@Composable
private fun LayoutTest() {
    Column(modifier = Modifier.fillMaxSize()) {
        BottomSectionWrapperLayout(
            modifier = Modifier.border(2.dp, Color.Red),
            content = {
                Text("Content Section")
            },
            bottomSection = {
                Text("Bottom Section")
            }
        )

        Spacer(Modifier.height(20.dp))
        BottomSectionWrapperLayout(
            modifier = Modifier.border(2.dp, Color.Red),
            bottomSection = {
                Text("Bottom Section")
            }
        )

        Spacer(Modifier.height(20.dp))
        BottomSectionWrapperLayout(
            modifier = Modifier.border(2.dp, Color.Red),
            content = {
                Text("Content Section")
            }
        )
   
    }
}

如果你检查println,你可以看到,在每种情况下,你将能够看到什么是null或不是。你也可以得到Placable尺寸。
另一个选项是像Textfield在第495行中那样创建这个布局。
https://cs.android.com/androidx/platform/frameworks/support/+/androidx-main:compose/material/material/src/commonMain/kotlin/androidx/compose/material/TextField.kt;l=495?q=TextField&ss=androidx%2Fplatform%2Fframeworks%2Fsupport
但是使用这种方法,您将无法检查是否传递了空lambda,因为它在非null时被放置在Box中。但是,这种方法经常使用,因为您可以通过ID而不是检查列表来检查和比较内容。

@Composable
fun BottomSectionWrapperLayout2(
    modifier: Modifier = Modifier,
    content: @Composable (() -> Unit)? = null,
    bottomSection: @Composable (() -> Unit)? = null,
) {

    Layout(
        modifier = modifier,
        content = {

            if (content != null) {
                Box(
                    modifier = Modifier.layoutId("content")
                ) {
                    content()
                }
            }

            if (bottomSection != null) {
                Box(
                    modifier = Modifier.layoutId("bottomSection")
                ) {
                    bottomSection()
                }
            }
        }
    ) { measurables: List<Measurable>, constraints: Constraints ->

        val looseConstraints = constraints.copy(minWidth = 0, minHeight = 0)

        val contentPlaceable: Placeable? =
            measurables.find { it.layoutId == "content" }?.measure(looseConstraints)
        val bottomSectionPlaceable =
            measurables.find { it.layoutId == "bottomSection" }?.measure(looseConstraints)

        // you can set width and height as required, i make it as a Column
        // this width and height determines whether it's a Column, Row or a Box

        val contentWidth = contentPlaceable?.width ?: 0
        val bottomSectionWidth = bottomSectionPlaceable?.width ?: 0

        val contentHeight = contentPlaceable?.height ?: 0
        val bottomSectionHeight = bottomSectionPlaceable?.height ?: 0

        val totalWidth = if (constraints.hasFixedWidth && constraints.hasBoundedWidth)
            constraints.maxWidth else contentWidth.coerceAtLeast(bottomSectionWidth)
            .coerceIn(constraints.minWidth, constraints.maxWidth)

        val totalHeight = if (constraints.hasFixedHeight && constraints.hasBoundedHeight)
            constraints.maxHeight else (contentHeight + bottomSectionHeight)
            .coerceIn(constraints.minHeight, constraints.maxHeight)

        layout(totalWidth, totalHeight) {
            contentPlaceable?.placeRelative(0, 0)
            bottomSectionPlaceable?.placeRelative(0, contentHeight)
        }
    }
}

用途与其他布局相同。

相关问题