Jetpack Compose Text 超链接部分文本

Posted

技术标签:

【中文标题】Jetpack Compose Text 超链接部分文本【英文标题】:Jetpack Compose Text hyperlink some section of the text 【发布时间】:2021-04-10 12:36:49 【问题描述】:

如何在 Text 组件的部分文本中添加超链接?

使用buildAnnotatedString,我可以将 link 部分设置为蓝色并加下划线,如下图所示,但我怎样才能将该部分转换为链接?

   val annotatedLinkString = buildAnnotatedString 
        val str = "Click this link to go to web site"
        val startIndex = str.indexOf("link")
        val endIndex = startIndex + 4
        append(str)
        addStyle(
            style = SpanStyle(
                color = Color(0xff64B5F6),
                textDecoration = TextDecoration.Underline
            ), start = startIndex, end = endIndex
        )
    

    Text(
        modifier = modifier
            .padding(16.dp)
            .fillMaxWidth(),
        text = annotatedLinkString
    )

我也可以得到Spanned,但有什么方法可以将它与Text 一起使用?

val str: Spanned = htmlCompat.fromHtml(
    "<a href=\"http://www.github.com\">Github</a>", HtmlCompat.FROM_HTML_MODE_LEGACY
)

【问题讨论】:

【参考方案1】:

要获得完整的答案,您可以使用返回文本位置的ClickableText,并使用UriHandler 在浏览器中打开 URI。

val annotatedLinkString: AnnotatedString = buildAnnotatedString 

    val str = "Click this link to go to web site"
    val startIndex = str.indexOf("link")
    val endIndex = startIndex + 4
    append(str)
    addStyle(
        style = SpanStyle(
            color = Color(0xff64B5F6),
            fontSize = 18.sp,
            textDecoration = TextDecoration.Underline
        ), start = startIndex, end = endIndex
    )

    // attach a string annotation that stores a URL to the text "link"
    addStringAnnotation(
        tag = "URL",
        annotation = "https://github.com",
        start = startIndex,
        end = endIndex
    )



// UriHandler parse and opens URI inside AnnotatedString Item in Browse
val uriHandler = LocalUriHandler.current

// ? Clickable text returns position of text that is clicked in onClick callback
ClickableText(
    modifier = modifier
        .padding(16.dp)
        .fillMaxWidth(),
    text = annotatedLinkString,
    onClick = 
        annotatedLinkString
            .getStringAnnotations("URL", it, it)
            .firstOrNull()?.let  stringAnnotation ->
                uriHandler.openUri(stringAnnotation.item)
            
    
)

【讨论】:

如何使用字符串资源进行这项工作,这似乎是硬编码字符串的好方法。【参考方案2】:

标注的答案让新手很困惑,我举个完整的例子

请不要忘记以pop()结束pushStringAnnotation

val annotatedString = buildAnnotatedString 
    append("By joining, you agree to the ")

    pushStringAnnotation(tag = "policy", annotation = "https://google.com/policy")
    withStyle(style = SpanStyle(color = MaterialTheme.colors.primary)) 
        append("privacy policy")
    
    pop()

    append(" and ")

    pushStringAnnotation(tag = "terms", annotation = "https://google.com/terms")

    withStyle(style = SpanStyle(color = MaterialTheme.colors.primary)) 
        append("terms of use")
    

    pop()


ClickableText(text = annotatedString, style = MaterialTheme.typography.body1, onClick =  offset ->
    annotatedString.getStringAnnotations(tag = "policy", start = offset, end = offset).firstOrNull()?.let 
        Log.d("policy URL", it.item)
    

    annotatedString.getStringAnnotations(tag = "terms", start = offset, end = offset).firstOrNull()?.let 
        Log.d("terms URL", it.item)
    
)

最终效果

【讨论】:

不错!您只是忘记在第一个 pushStringAnnotation 和 withStyle 方法对之后调用 pop()。 有没有办法为这样的视图添加一些选定的状态/涟漪效果?现在它看起来完全是静态的 @ZdenekSima 谢谢提醒,我更新了答案【参考方案3】:

对于寻求可重复使用的复制粘贴解决方案的任何人,

创建一个新文件LinkText.kt 并复制粘贴此代码,

data class LinkTextData(
    val text: String,
    val tag: String? = null,
    val annotation: String? = null,
    val onClick: ((str: AnnotatedString.Range<String>) -> Unit)? = null,
)

@Composable
fun LinkText(
    linkTextData: List<LinkTextData>,
    modifier: Modifier = Modifier,
) 
    val annotatedString = createAnnotatedString(linkTextData)

    ClickableText(
        text = annotatedString,
        style = MaterialTheme.typography.body1,
        onClick =  offset ->
            linkTextData.forEach  annotatedStringData ->
                if (annotatedStringData.tag != null && annotatedStringData.annotation != null) 
                    annotatedString.getStringAnnotations(
                        tag = annotatedStringData.tag,
                        start = offset,
                        end = offset,
                    ).firstOrNull()?.let 
                        annotatedStringData.onClick?.invoke(it)
                    
                
            
        ,
        modifier = modifier,
    )


@Composable
private fun createAnnotatedString(data: List<LinkTextData>): AnnotatedString 
    return buildAnnotatedString 
        data.forEach  linkTextData ->
            if (linkTextData.tag != null && linkTextData.annotation != null) 
                pushStringAnnotation(
                    tag = linkTextData.tag,
                    annotation = linkTextData.annotation,
                )
                withStyle(
                    style = SpanStyle(
                        color = MaterialTheme.colors.primary,
                        textDecoration = TextDecoration.Underline,
                    ),
                ) 
                    append(linkTextData.text)
                
                pop()
             else 
                append(linkTextData.text)
            
        
    

用法

LinkText(
    linkTextData = listOf(
        LinkTextData(
            text = "Icons made by ",
        ),
        LinkTextData(
            text = "smalllikeart",
            tag = "icon_1_author",
            annotation = "https://www.flaticon.com/authors/smalllikeart",
            onClick = 
                Log.d("Link text", "$it.tag $it.item")
            ,
        ),
        LinkTextData(
            text = " from ",
        ),
        LinkTextData(
            text = "Flaticon",
            tag = "icon_1_source",
            annotation = "https://www.flaticon.com/",
            onClick = 
                Log.d("Link text", "$it.tag $it.item")
            ,
        )
    ),
    modifier = Modifier
        .padding(
            all = 16.dp,
        ),
)

截图

注意

    我正在使用可组合项手动处理网页。如果不需要手动控制,请使用UriHandler 或其他替代方法。 LinkText 中要求的可点击和其他文本样式。

【讨论】:

tag有什么用? @Marat, tag 就像 id。用于识别注解 - 文档:developer.android.com/reference/kotlin/androidx/compose/ui/text/… 我明白了。非常感谢【参考方案4】:

如何在 Text 组件的部分文本中添加超链接?

with(AnnotatedString.Builder()) 
    append("link: Jetpack Compose")
    // attach a string annotation that stores a URL to the text "Jetpack Compose".
    addStringAnnotation(
        tag = "URL",
        annotation = "https://developer.android.com/jetpack/compose",
        start = 6,
        end = 21
    )

标签:用于区分注解的标签

annotation:附加的字符串注解

start:范围的包含起始偏移量

end

的独占结束偏移量

Source

【讨论】:

除了“URL”还有什么其他标签? 对不起。我误解了。我也是刚学的。它需要4个参数。谢谢你的好问题。 我用 annotatedString() 尝试了这个并设置为Text,添加了互联网权限以显示但它不起作用,我的意思是当你触摸文本时什么都没有发生。介意检查一下吗? 您需要使用 url 处理程序。【参考方案5】:

您可以使用https://github.com/firefinchdev/linkify-text

它是一个文件,你可以直接将它复制到你的项目中。

另外,它使用Android的Linkify进行链接检测,与TextViewautoLink相同。

【讨论】:

以上是关于Jetpack Compose Text 超链接部分文本的主要内容,如果未能解决你的问题,请参考以下文章

Jetpack Compose 无限加载列表(滚到底部自动加载更多)

屏幕滚动到顶部(Jetpack Compose 分页)

Android全新UI编程 - Jetpack Compose 超详细教程

Jetpack Compose draw text 笔记

Jetpack Compose draw text 笔记

自学Jetpack Compose 系列Compose控件Text与TextStyle的学习与使用