使用撰写文本进行链接

ago*_*st_ 11 android kotlin android-jetpack androidx android-jetpack-compose

我找不到如何链接我Text()使用的 Jetpack Compose。

在撰写之前我要做的就是:

Linkify.addLinks(myTextView, Linkify.EMAIL_ADDRESSES or Linkify.WEB_URLS)

显然,我的 TextView 中包含的所有链接都变成了可点击的链接。

重要提示:文本内容来自 API,链接没有固定位置,内容可能包含多个链接。

我想通过使用 Jetpack Compose 来保持这种行为,但我找不到任何有关这样做的信息。

有人知道吗?

ago*_*st_ 13

如果有人正在寻找解决方案,以下将使任何链接可点击并在文本中设置样式:

@Composable
fun LinkifyText(text: String, modifier: Modifier = Modifier) {
    val uriHandler = LocalUriHandler.current
    val layoutResult = remember {
        mutableStateOf<TextLayoutResult?>(null)
    }
    val linksList = extractUrls(text)
    val annotatedString = buildAnnotatedString {
        append(text)
        linksList.forEach {
            addStyle(
                style = SpanStyle(
                    color = Color.Companion.Blue,
                    textDecoration = TextDecoration.Underline
                ),
                start = it.start,
                end = it.end
            )
            addStringAnnotation(
                tag = "URL",
                annotation = it.url,
                start = it.start,
                end = it.end
            )
        }
    }
    Text(
        text = annotatedString, 
        style = MaterialTheme.typography.body1,
        onTextLayout = { layoutResult.value = it },
        modifier = modifier
            .pointerInput(Unit) {
                detectTapGestures { offsetPosition ->
                    layoutResult.value?.let {
                        val position = it.getOffsetForPosition(offsetPosition)
                        annotatedString.getStringAnnotations(position, position).firstOrNull()
                            ?.let { result ->
                                if (result.tag == "URL") {
                                    uriHandler.openUri(result.item)
                                }
                            }
                    }
                }
            }
    )
}

private val urlPattern: Pattern = Pattern.compile(
        "(?:^|[\\W])((ht|f)tp(s?):\\/\\/|www\\.)"
                + "(([\\w\\-]+\\.){1,}?([\\w\\-.~]+\\/?)*"
                + "[\\p{Alnum}.,%_=?&#\\-+()\\[\\]\\*$~@!:/{};']*)",
        Pattern.CASE_INSENSITIVE or Pattern.MULTILINE or Pattern.DOTALL
)

fun extractUrls(text: String): List<LinkInfos> {
    val matcher = urlPattern.matcher(text)
    var matchStart: Int
    var matchEnd: Int
    val links = arrayListOf<LinkInfos>()

    while (matcher.find()) {
        matchStart = matcher.start(1)
        matchEnd = matcher.end()

        var url = text.substring(matchStart, matchEnd)
        if (!url.startsWith("http://") && !url.startsWith("https://"))
            url = "https://$url"

        links.add(LinkInfos(url, matchStart, matchEnd))
    }
    return links
}

data class LinkInfos(
        val url: String,
        val start: Int,
        val end: Int
)
Run Code Online (Sandbox Code Playgroud)

  • 它可以处理诸如 tel: 和 mailto: 之类的事情吗? (3认同)

小智 9

您仍然可以使用Linkify.addLinks,但将结果转换为AnnotatedString如下所示:

fun String.linkify(
    linkStyle: SpanStyle,
) = buildAnnotatedString {
    append(this@linkify)

    val spannable = SpannableString(this@linkify)
    Linkify.addLinks(spannable, Linkify.WEB_URLS)

    val spans = spannable.getSpans(0, spannable.length, URLSpan::class.java)
    for (span in spans) {
        val start = spannable.getSpanStart(span)
        val end = spannable.getSpanEnd(span)

        addStyle(
            start = start,
            end = end,
            style = linkStyle,
        )
        addStringAnnotation(
            tag = "URL",
            annotation = span.url,
            start = start,
            end = end
        )
    }
}

fun AnnotatedString.urlAt(position: Int, onFound: (String) -> Unit) =
    getStringAnnotations("URL", position, position).firstOrNull()?.item?.let {
        onFound(it)
    }
Run Code Online (Sandbox Code Playgroud)

在您的可组合项中使用它,如下所示:

val linkStyle = SpanStyle(
    color = MaterialTheme.colors.primary,
    textDecoration = TextDecoration.Underline,
)
ClickableText(
    text = remember(text) { text.linkify(linkStyle) },
    onClick = { position -> text.urlAt(position, onClickLink) },
)
Run Code Online (Sandbox Code Playgroud)


Jos*_*e S 6

我认为现在更好的解决方案是使用 textview 创建自己的组件,如下所示:

@Composable
fun DefaultLinkifyText(modifier: Modifier = Modifier, text: String?) {
    val context = LocalContext.current
    val customLinkifyTextView = remember {
       TextView(context)
    }
    AndroidView(modifier = modifier, factory = { customLinkifyTextView }) { textView ->
        textView.text = text ?: ""
        LinkifyCompat.addLinks(textView, Linkify.ALL)
        Linkify.addLinks(textView, Patterns.PHONE,"tel:",
            Linkify.sPhoneNumberMatchFilter, Linkify.sPhoneNumberTransformFilter)
        textView.movementMethod = LinkMovementMethod.getInstance()
    }
}
Run Code Online (Sandbox Code Playgroud)