android - Jetpack compose - 在文本后面绘制背景
问题描述
我想AnnotatedString
在 Jetpack Compose 中绘制一些背景(此处为示例)。使用视图系统,我们可以通过编写自定义文本视图来做到这一点 - https://medium.com/androiddevelopers/drawing-a-rounded-corner-background-on-text-5a610a95af5。有没有办法使用 Jetpack Compose 做到这一点?
我正在查看draw
修饰符,Text
但我似乎无法弄清楚如何获取行号或我需要在其上绘制背景的文本的开始/结束。
我应该使用Canvas
而不是Text
吗?
解决方案
Text
字符布局信息的主要来源是TextLayoutResult
可以通过onTextLayout
参数接收
它的 API 远非完美。getPathForRange
返回Path
这正是您所需要的,但与其他任何东西一样,Path
它不能被修改,例如,您将无法绕过这条路径的拐角,与普通SpanStyle
背景没有太大区别
那里getBoundingBox
只返回一个字符的帧。我玩了一下,得到Rect
了选定范围的 s 列表:
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
:
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,
)
结果:
推荐阅读
- android - 无法为 Kotlin 中的视图设置背景颜色
- flutter - 如何使用 Flutter Here SDK 检测地图中的平移手势
- python - 在 mac 中安装 conda 后的 .*shrc 和 .*profile 文件
- excel - 打开 Excel 密码恢复
- node.js - 如何使用“非原子”批处理/管道在 Scylla 中运行多个查询
- kivy - Kivy WebView 错误:无法添加到窗口,它已经有一个父级
- julia - 朱莉娅情节中的注释
- sql-server - 选择逻辑读取或执行时间较短的 SQL Server 查询
- php - 注意:尝试访问 null 类型值的数组偏移量
- html - 大屏幕响应式显示