Jetpack compose - 在文本后面绘制背景 [英] Jetpack compose - draw background behind text

查看:98
本文介绍了Jetpack compose - 在文本后面绘制背景的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在 Jetpack Compose (此处示例).使用视图系统,我们可以通过编写自定义文本视图来实现 - https://medium.com/androiddevelopers/drawing-a-rounded-corner-background-on-text-5a610a95af5.有没有办法使用 Jetpack Compose 来做到这一点?

I would like to draw a background behind some AnnotatedString in Jetpack Compose (Example here). Using the view system, we could do this by writing a custom text view - https://medium.com/androiddevelopers/drawing-a-rounded-corner-background-on-text-5a610a95af5. Is there a way to do this using Jetpack Compose?

我正在查看 Textdraw 修饰符,但我似乎无法弄清楚如何获取行号或文本的开始/结束我需要绘制背景.

I was looking at the draw modifiers for Text but I can't seem to figure out how to get the line number or the start/end of the text I need to draw the background on.

我应该使用 Canvas 而不是 Text 吗?

Should I use Canvas instead of Text?

推荐答案

Text 字符布局的主要信息来源是 TextLayoutResult ,可以通过 接收onTextLayout 参数

The main source of information about Text characters layout is TextLayoutResult which can be received with onTextLayout parameter

它的 API 远非完美.getPathForRange 返回 Path 这正是您所需要的,但与任何其他 Path 一样,它不能被修改,例如你将无法圆角这条路径,与普通 SpanStyle 背景没有太大区别

It's API is far from perfect. getPathForRange returns Path which is exactly what you need, but as any other Path it cannot be modified, e.g. you won't be able to round corners of this path, not much difference from plain SpanStyle background

getBoundingBox 只返回一个字符的帧.我玩了它,并获得了所选范围的 Rect 列表:

There's getBoundingBox which only returns frame of one character. I played with it, and got the list of Rects for a selected range:

fun TextLayoutResult.getBoundingBoxesForRange(start: Int, end: Int): List<Rect> {
    var prevRect: Rect? = null
    var firstLineCharRect: Rect? = null
    val boundingBoxes = mutableListOf<Rect>()
    for (i in start..end) {
        val rect = getBoundingBox(i)
        val isLastRect = i == end
        
        // single char case
        if (isLastRect && firstLineCharRect == null) {
            firstLineCharRect = rect
            prevRect = rect
        }

        // `rect.right` is zero for the last space in each line
        // looks like an issue to me, reported: https://issuetracker.google.com/issues/197146630
        if (!isLastRect && rect.right == 0f) continue

        if (firstLineCharRect == null) {
            firstLineCharRect = rect
        } else if (prevRect != null) {
            if (prevRect.bottom != rect.bottom || isLastRect) {
                boundingBoxes.add(
                    firstLineCharRect.copy(right = prevRect.right)
                )
                firstLineCharRect = rect
            }
        }
        prevRect = rect
    }
    return boundingBoxes
}

现在您可以在 Canvas 上绘制这些矩形:

Now you can draw these rects on the Canvas:

Box(Modifier.padding(10.dp)) {
    val text =
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
    val selectedParts = listOf(
        "consectetur adipiscing",
        "officia deserunt",
        "dolore magna aliqua. Ut enim ad minim veniam, quis nostrud",
        "consequat.",
    )
    var selectedPartPaths by remember { mutableStateOf(listOf<Path>()) }
    Text(
        text,
        style = MaterialTheme.typography.h6,
        onTextLayout = { layoutResult ->
            selectedPartPaths = selectedParts.map { part ->
                val cornerRadius = CornerRadius(x = 20f, y = 20f)
                Path().apply {
                    val startIndex = text.indexOf(part)
                    val boundingBoxes = layoutResult
                        .getBoundingBoxesForRange(
                            start = startIndex,
                            end = startIndex + part.count()
                        )
                    for (i in boundingBoxes.indices) {
                        val boundingBox = boundingBoxes[i]
                        val leftCornerRoundRect =
                            if (i == 0) cornerRadius else CornerRadius.Zero
                        val rightCornerRoundRect =
                            if (i == boundingBoxes.indices.last) cornerRadius else CornerRadius.Zero
                        addRoundRect(
                            RoundRect(
                                boundingBox.inflate(verticalDelta = -2f, horizontalDelta = 7f),
                                topLeft = leftCornerRoundRect,
                                topRight = rightCornerRoundRect,
                                bottomRight = rightCornerRoundRect,
                                bottomLeft = leftCornerRoundRect,
                            )
                        )
                    }
                }
            }
        },
        modifier = Modifier.drawBehind {
            selectedPartPaths.forEach { path ->
                drawPath(path, style = Fill, color = Color.Blue.copy(alpha = 0.2f))
                drawPath(path, style = Stroke(width = 2f), color = Color.Blue)
            }
        }
    )
}

fun Rect.inflate(verticalDelta: Float, horizontalDelta: Float) =
    Rect(
        left = left - horizontalDelta,
        top = top - verticalDelta,
        right = right + horizontalDelta,
        bottom = bottom + verticalDelta,
    )

结果:

这篇关于Jetpack compose - 在文本后面绘制背景的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆