【问题标题】:How to use xml:s android:id in Jetpack compose?如何在 Jetpack Compose 中使用 xml:s android:id?
【发布时间】:2022-12-09 14:35:11
【问题描述】:

我怎样才能像这样使用 xml:s android:id

<TextView android:id="@id/randomId">

在 Jetpack 中撰写?我在想这样的事情:

Text(modifier = Modifier.layoutId("randomId"), text = text)

我问是因为我使用可以识别 android:ids 的 Appium,我也想对 Compose 做同样的事情。

【问题讨论】:

    标签: android xml kotlin android-jetpack-compose appium-android


    【解决方案1】:

    Jetpack Compose 是一个声明式 Ui,您可以使用带有 @Composable 注释的函数创建 Ui 组件,这与您创建 View 或 ViewGroup 实例的 View 系统不同,您可以使用您定义的 id 获取它以设置属性。在 Jetpack Compose 中,您将 Composable 的属性声明为函数的参数。

    Modifier.layoutId 在您构建具有特定布局的自定义可组合项时使用,以计算子可组合项的尺寸和位置。

    /**
     * Retrieves the tag associated to a composable with the [Modifier.layoutId] modifier.
     * For a parent data value to be returned by this property when not using the [Modifier.layoutId]
     * modifier, the parent data value should implement the [LayoutIdParentData] interface.
     *
     * Example usage:
     * @sample androidx.compose.ui.samples.LayoutTagChildrenUsage
     */
    val Measurable.layoutId: Any?
        get() = (parentData as? LayoutIdParentData)?.layoutId
    

    总而言之,当您构建自定义布局时,它就像

    @Composable
    private fun MyLayout(
        modifier: Modifier = Modifier,
        content: @Composable () -> Unit
    ) {
    
        Layout(
            modifier = modifier,
            content = content
        ) { measurables: List<Measurable>, constraints: Constraints ->
    
            val placeables: List<Placeable> = measurables.map { measurable: Measurable ->
                measurable.measure(constraints)
            }
    
            // Width and height of parent composable depends on requirements
            layout(constraints.maxWidth, constraints.maxHeight) {
                placeables.forEach { placeable: Placeable ->
                    // place composables based on requirements vertically, horizontally, diagonal, etc.
                }
            }
        }
    }
    

    1- 您创建一个布局,它需要一个我们用 lambda 设置的 MeasurePolicy,并通过 layout(width, height) 函数返回 MeasureResult。

    2- 约束是由大小修改器、滚动修改器或父级选择限制大小的方式设置的限制。 In Constraints section 我解释了哪个修饰符返回哪个绑定。您只能在这些范围内测量可测量的。

    但是,如果您不让 Composable 通过 constraints.copy()Constraints.fixed()constraints.offset() 选择不同的边界,则可以修改 Constraints。一件重要的事情最大界限仍然在父母的最大界限内。

    然后你只测量一次你的可测量,如果你再次尝试测量你会得到一个例外.

    但是,有时您需要根据另一个 Composable 设置 Comosable 的尺寸。例如,如果 Image 是在运行时设置的 100.dp,而您希望将 Text 调整为 100.dp,并且您不知道在内容中设置了哪个顺序 ImageText

    您需要使用 SubcomposeLayout,它使用 subcompose 函数来重新测量可组合项,或者您可以使用 Modifier.layoutId 来确定您首先测量的可组合项,如下面的示例或此答案的 Selectively measuring to match one Sibling to Another section 中所示

    @Composable
    private fun MyLayout(
        modifier: Modifier = Modifier,
        content: @Composable () -> Unit
    ) {
    
        Layout(
            modifier = modifier,
            content = content
        ) { measurables: List<Measurable>, constraints: Constraints ->
    
            val imageMeasurable: Measurable = measurables.find { it.layoutId == "image" }!!
            val textMeasurable: Measurable = measurables.find { it.layoutId == "text" }!!
    
            val imagePlaceable = imageMeasurable.measure(constraints.copy(minWidth = 0, minHeight = 0))
    
            // Limit text width to image width by setting min and max width at image width
            val textPlaceable = textMeasurable.measure(
                constraints.copy(
                    minWidth = imagePlaceable.width,
                    maxWidth = imagePlaceable.width
                )
            )
    
            val width = imagePlaceable.width
            val imagePlaceableHeight = imagePlaceable.height
            val height = imagePlaceableHeight + textPlaceable.height
            // Width and height of parent composable depends on requirements
            layout(width, height) {
                imagePlaceable.placeRelative(0, 0)
                textPlaceable.placeRelative(0, imagePlaceableHeight)
            }
        }
    }
    

    示范

    MyLayout(
        modifier = Modifier
            .fillMaxWidth()
            .border(1.dp, Color.Cyan)
    ) {
        Image(
            modifier = Modifier
                .size(150.dp)
                .layoutId("image"),
            painter = painterResource(id = R.drawable.landscape1),
            contentDescription = null,
            contentScale = ContentScale.FillBounds
        )
    
        Text(
            modifier = Modifier
                .layoutId("text")
                .border(2.dp, Color.Red),
            text = "Hello world some very long text that will be scaled"
        )
    }
    
    MyLayout(
        modifier = Modifier
            .fillMaxWidth()
            .border(1.dp, Color.Cyan)
    ) {
    
        Text(
            modifier = Modifier
                .layoutId("text")
                .border(2.dp, Color.Red),
            text = "Hello world some very long text that will be scaled"
        )
    
        Image(
            modifier = Modifier
                .size(100.dp)
                .layoutId("image"),
            painter = painterResource(id = R.drawable.landscape1),
            contentDescription = null,
            contentScale = ContentScale.FillBounds
        )
    
    }
    

    结果

    如图所示,我们首先使用 layoutId 获取 Image,然后测量 Text 以使其宽度与 Image 匹配。我认为这不适用于 appium,除非它有某种检查员 Modifier

    【讨论】:

    • 很好的解释,但它没有回答我关于如何在 Jetpack compose 中使用 xml:s android:id 的问题
    • 这是您可以在 Jetpack Compose 中使用的最接近的。正如我在 declerative ui 中解释的那样,不需要 id,因为它们都是函数,而不是从 xml 解析后获得的对象实例。如果它使用检查器获取 Modifier 并从中获取 id,则需要查看 appium 文档。如果您查看任何可组合项目,您会发现几乎从未使用过 Modifier.layoutId
    • Composable 的属性是在层次结构中添加函数时设置的,而不是通过获取对象实例来设置。 Column(// Properties declared here){} 不是通过标签或 id 获取此函数的实例
    • @Thracian 很好的解释来理解基本知识..
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-15
    • 2021-07-14
    • 2020-04-23
    • 1970-01-01
    • 2022-08-18
    • 1970-01-01
    相关资源
    最近更新 更多