如何在将可组合项显示为InlineTextContent之前测量可组合项?

huangapple go评论45阅读模式
英文:

How to measure a Composable before displaying it as InlineTextContent?

问题

我想在AnnotatedString中将Composable用作InlineTextContent。这需要为Placeholder提供widthheight。有没有一种方法可以在它被替换并显示之前预先测量我的Composable的宽度和高度?

英文:

I want to use a Composable as InlineTextContent in an AnnotatedString. This requires supplying a width and height to the Placeholder. Is there a way to pre-measure my Composable to know the width and height before it is swapped in and displayed?

答案1

得分: 1

我理解了你的请求。以下是你提供的代码的中文翻译:

我猜测Placeholder要求指定大小是因为你可以传递像1.ep这样的值给高度 - 在这种情况下,它将等于字体大小的高度,因此您可以相应地缩放内容。

但一般情况下,我使用SubcomposeLayout - 您可以测量任何视图的大小,并根据结果对实际视图进行子组合:

@Composable
fun 测量视图大小(
    要测量的视图: @Composable () -> Unit,
    修饰符: Modifier = Modifier,
    内容: @Composable (DpSize) -> Unit,
) {
    SubcomposeLayout(modifier = 修饰符) { 约束 ->
        val 测量大小 = subcompose("要测量的视图") {
            要测量的视图()
        }[0].measure(约束)
            .let {
                DpSize(
                    宽度 = it.width.toDp(),
                    高度 = it.height.toDp()
                )
            }

        val 内容可放置 = subcompose("内容") {
            内容(测量大小)
        }.firstOrNull()?.measure(约束)

        layout(内容可放置?.宽度 ?: 0, 内容可放置?.高度 ?: 0) {
            内容可放置?.place(0, 0)
        }
    }
}

要与InlineTextContent一起使用,您还需要将Dp转换为Sp

@Composable
fun 视图() {
    val 我的ID = "inlineContent"
    val 文本 = buildAnnotatedString {
        append("你好")
        // 追加一个占位符字符串“[myBox]”并在其上附加一个注释“inlineContent”。
        appendInlineContent(我的ID, "[myBox]")
    }
    val 密度 = LocalDensity.current

    测量视图大小(
        要测量的视图 = {
            内联视图()
        },
    ) { 测量大小 ->
        val 内联内容 = mapOf(
            Pair(
                // 这告诉[BasicText]要用[InlineTextContent]对象中给定的组合替换占位符字符串“[myBox]”。
                我的ID,
                InlineTextContent(
                    // 占位符告诉文本布局预期大小和垂直对齐方式
                    with(密度) {
                        Placeholder(
                            宽度 = 测量大小.width.toSp(),
                            高度 = 测量大小.height.toSp(),
                            placeholderVerticalAlign = PlaceholderVerticalAlign.Center
                        )
                    }
                ) {
                    内联视图()
                }
            )
        )

        BasicText(
            text = 文本,
            inlineContent = 内联内容,
        )
    }
}

@Composable
private fun 内联视图() {
    Box(
        modifier = Modifier
            .height(100.dp)
            .aspectRatio(0.5f)
            .background(color = Color.Red)
    )
}

结果:

英文:

I guess that the reason Placeholder asks for size that you can pass something like 1.ep value to height - in this case it's gonna be equal to the font size height, so you can scale your content accordingly.

But generally for such purpose I'm using SubcomposeLayout - you can measure any view size and subcompose actual view depending on the result:

@Composable
fun MeasureViewSize(
    viewToMeasure: @Composable () -> Unit,
    modifier: Modifier = Modifier,
    content: @Composable (DpSize) -> Unit,
) {
    SubcomposeLayout(modifier = modifier) { constraints ->
        val measuredSize = subcompose("viewToMeasure") {
            viewToMeasure()
        }[0].measure(constraints)
            .let {
                DpSize(
                    width = it.width.toDp(),
                    height = it.height.toDp()
                )
            }

        val contentPlaceable = subcompose("content") {
            content(measuredSize)
        }.firstOrNull()?.measure(constraints)

        layout(contentPlaceable?.width ?: 0, contentPlaceable?.height ?: 0) {
            contentPlaceable?.place(0, 0)
        }
    }
}

To use it with InlineTextContent you also need to convert Dp to Sp:

@Composable
fun View() {
    val myId = "inlineContent"
    val text = buildAnnotatedString {
        append("Hello")
        // Append a placeholder string "[myBox]" and attach an annotation "inlineContent" on it.
        appendInlineContent(myId, "[myBox]")
    }
    val density = LocalDensity.current

    MeasureViewSize(
        viewToMeasure = {
            ViewToInline()
        },
    ) { measuredSize ->
        val inlineContent = mapOf(
            Pair(
                // This tells the [BasicText] to replace the placeholder string "[myBox]" by
                // the composable given in the [InlineTextContent] object.
                myId,
                InlineTextContent(
                    // Placeholder tells text layout the expected size and vertical alignment of
                    // children composable.
                    with(density) {
                        Placeholder(
                            width = measuredSize.width.toSp(),
                            height = measuredSize.height.toSp(),
                            placeholderVerticalAlign = PlaceholderVerticalAlign.Center
                        )
                    }
                ) {
                    ViewToInline()
                }
            )
        )

        BasicText(
            text = text, 
            inlineContent = inlineContent,
        )
    }
}

@Composable
private fun ViewToInline() {
    Box(
        modifier = Modifier
            .height(100.dp)
            .aspectRatio(0.5f)
            .background(color = Color.Red)
    )
}

Result:

<img src="https://i.stack.imgur.com/VvPpQ.png" width="80"></img>

huangapple
  • 本文由 发表于 2023年6月1日 02:18:17
  • 转载请务必保留本文链接:https://go.coder-hub.com/76376304.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定