ITmob-Ly
发布于 2023-03-07 / 291 阅读
0

怎样处理 Jetpack Compose 中文本的长按选择和点击事件(如何在 Text 中添加超链接?)

1. 介绍

Jetpack Compose 是一个用于构建原生 Android UI 的现代工具包。任何 UI 最重要的组成部分之一就是文本。Jetpack Compose 的 Text 如何显示文本的基本用法就不再赘述,主要介绍 Text 怎样实现长按文本可选择、怎样处理文本的点击事件(文本整体的点击和部分文本的点击,比如:如何在 Text 中添加超链接?)。

2. 实现文本长按可选择

默认 Text 是无法选择的,Jetpack Compose 中提供了一个可组合项:SelectionContainer 为其直接或间接子项启用文本选择。

@Composable
fun SelectableText() {
    SelectionContainer {
        Text("Selectable text. Hello itmob.cn")
    }
}

Selectable Text

3. 实现可点击的文本

3.1 使用 Modifier.clickable

最简单的方法是使用 Modifier 提供的 clickable 方法来实现和处理文本的点击事件,但这样实现点击时会有类似于按钮的背景颜色变化和水波涟漪的效果。

@Composable
fun HandleClickWithModifier() {
    Text(
        modifier = Modifier.clickable {
            // TODO Handle click event here
        },
        text = "Hello itmob.cn"
    )
}

3.2 使用 ClickableText

如果需要判断点击的位置,官方文档推荐使用 ClickableText 这个可组合项,它接受一个 AnnotatedString 类型的实例。可以通过 onClick 回调获得点击在文本可组合项中的位置,这样就可以根据文本的不同部分执行不同的操作。

@Composable
fun HandleClickWithClickableText() {
    ClickableText(text = AnnotatedString("Hello itmob.cn")) { offset ->
        // TODO Handle click event here
        Log.d("ClickableText", "$offset -th character is clicked.")
    }
}

上面是 ClickableText 最简单的实现,可以在回调方法中获得被点击的文本位置

3.3 使用 ClickableText 在文本内实现超链接效果

当用户点击文本时,可能还希望将额外信息附加到文本值的一部分,例如:给部分文本添加超链接,点击时打开特定的 URL。

为实现它,可以通过 AnnotatedString 的 pushStringAnnotation()pop() 方法为特定的文本添加注释,在点击回调方法中可以使用它们的标签或文本范围来过滤获得这些注释。这就实现了点击特定文本,而且可以获得特定文本的注释中附加的参数。

@Composable
fun ClickableAnnotatedString() {
    val context = LocalContext.current
    val annotatedString = buildAnnotatedString {
        withStyle(SpanStyle(color = Color.Red, fontWeight = FontWeight.Bold)) {
            append("Hello ")
        }
        pushStringAnnotation("TAG_URL", "https://itmob.cn")
        withStyle(SpanStyle(color = Color.Blue, fontWeight = FontWeight.Bold)) {
            append("itmob.cn")
        }
        pop()
    }
    ClickableText(
        text = annotatedString,
        onClick = { offset ->
            val annotations = annotatedString.getStringAnnotations("TAG_URL", offset, offset)
            annotations.firstOrNull()?.let {
                Log.d("ClickableText", "$offset -th character is clicked.")
                context.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(it.item)))
            }
        }
    )
}

Handling click event in Jetpack Compose Text

需要注意的是, getStringAnnotations() 传了之前设置的tags参数的内容及开始和结束的下标,返回的是一个List对象,点击没有添加注释的地方也会回调 onclick 但是 annotations 是空集合,直接使用 annotations.first {} 方法会抛出 NoSuchElementException 异常

java.util.NoSuchElementException: List is empty.
    at kotlin.collections.CollectionsKt___CollectionsKt.first(_Collections.kt:212)
    at cn.itmob.example.MainActivityKt$ClickableAnnotatedString$1.invoke(MainActivity.kt:157)
    at cn.itmob.example.MainActivityKt$ClickableAnnotatedString$1.invoke(MainActivity.kt:153)
    at androidx.compose.foundation.text.ClickableTextKt$ClickableText$pressIndicator$1$1$1.invoke-k-4lQ0M(ClickableText.kt:78)
    at androidx.compose.foundation.text.ClickableTextKt$ClickableText$pressIndicator$1$1$1.invoke(ClickableText.kt:76)
    at androidx.compose.foundation.gestures.TapGestureDetectorKt$detectTapGestures$2$1$1.invokeSuspend(TapGestureDetector.kt:125)
    at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
    at kotlinx.coroutines.DispatchedTaskKt.resume(DispatchedTask.kt:178)
    at kotlinx.coroutines.DispatchedTaskKt.dispatch(DispatchedTask.kt:166)