我正在尝试向我的 Jetpack Compose 应用程序添加一项功能,当用户单击某个单词时,工具提示将出现在该单词的上方/下方,类似于 Duolingo:
到目前为止,我有一个 AnnotatedString,它可以在单击时打印单词。以下是我在给定输入字符串的情况下生成 AnnotatedString 的方法:
val input = // get some string input from user
val words = input.parseInput() // split a String into a list of words and spaces (e.g. "Hello world" -> ["Hello", " ", "world"])
data class AnnotatedWord(word: String, tag: String)
val annotatedWords = emptyList<AnnotatedWord>()
var wordIndex = 0
val annotatedString = buildAnnotatedString {
words.forEach { word ->
if (word == " ") {
append(word)
continue
}
val tag = "${word}:${wordIndex++}"
pushStringAnnotation(tag = tag, annotation = tag)
withStyle(style = SpanStyle(textDecoration = TextDecoration.Underline)) {
append(word)
}
pop()
annotatedWords.add(AnnotatedWord(word, tag))
}
}
这是我使用点击侦听器显示它的方式:
ClickableText(
text = annotatedString,
onClick = { index ->
val annotation = annotatedString.getStringAnnotations(start = index, end = index).firstOrNull()
if (annotation != null) {
val tag = annotation.tag
val matchingWord = annotatedWords.find { it.tag == tag }
print(matchingWord.word)
}
}
)
这效果很好 - 它给了我一个字符串,其中每个单词都带有下划线,然后单击一个单词即可打印它。然而我在工具提示部分遇到了困难。我想我可以通过将 ClickableText 嵌套在 Box 中,将工具提示也放入该 Box 中,并使用 Modifier.offset() 将其放置在单词上方来显示工具提示。但是,我不确定如何获取此偏移量,更不用说如何将其从 AnnotatedString 传递到我的可组合项了。
我最终这样做了:
// index = the index of the clicked character in the string
val wordBoundary = layoutResult.getWordBoundary(index)
val leftBox = layoutResult.getBoundingBox(wordBoundary.min)
val rightBox = layoutResult.getBoundingBox(wordBoundary.max - 1)
val middle = (leftBox.left + rightBox.right) / 2
val top = leftBox.top
val offset = Offset(x = middle, y = top)
val placeable = measurable.measure(constraints)
val width = placeable.width
val height = placeable.height
// set width/height to 0 so the tooltip doesn't take up any space in its container
layout(0, 0) {
val x = (offset.x - (width / 2)).toInt()
val y = (offset.y - height).toInt()
placeable.placeRelative(x, y)
}
最终看起来像这样: