Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Jetpack Compose Text hyperlink some section of the text

How can i add hyperlink to some section of the text of Text component?

With buildAnnotatedString i can set link section blue and underlined as in image below, but how can i also turn that section into link?

enter image description here

   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
    )

I can also get Spanned but is there any way to use it with Text?

val str: Spanned = HtmlCompat.fromHtml(
    "<a href=\"http://www.github.com\">Github</a>", HtmlCompat.FROM_HTML_MODE_LEGACY
)
like image 592
Thracian Avatar asked Jan 04 '21 17:01

Thracian


People also ask

How do I get TextField value in jetpack compose?

To read value entered in TextField in Android Compose, declare a variable, and assign this variable the value in the TextField whenever there is a change in the value. The same process holds for reading value entered in OutlineTextField composable.

How to create hyperlinks in jetpack compose?

Similarly, in Jetpack Compose, a Text element is used to display text on the activity screen. By default, Text cannot create a hyperlink around the text, So, we create a hyperlink using Annonated String and display it using Clickable Text.

What is displaying texts in jetpack compose?

Displaying Texts is one of the most basic requirements in any kind of UI framework, be it in mobile, desktop or web. And Jetpack Compose makes this easy and intuitive for us via the Text widget.

How to create a hyperlink at a particular text span in Android?

By default, Text cannot create a hyperlink around the text, So, we create a hyperlink using Annonated String and display it using Clickable Text. So in this article, we will show you how you could create a Hyperlink at a particular Text Span in Android using Jetpack Compose.

How to add custom fonts to Android Studio jetpack compose project?

Create an Android Studio Jetpack compose project. No third party dependencies are needed for this project. Add your custom fonts in the fonts folder. Create one under the res if it doesn't exist: Copy the code or download it in the link below, build and run. 1. 2. This example shows you not only how to display Text but also style it.


4 Answers

For a complete answer you can use ClickableText which returns the position of text, and UriHandler to open URI in a browser.

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)
            }
    }
)
like image 103
Thracian Avatar answered Oct 22 '22 09:10

Thracian


The marked answer confuses novices, I give a complete example

Please don't forget to end pushStringAnnotation with pop()

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)
    }
})

final effect

enter image description here

like image 39
gaohomway Avatar answered Oct 22 '22 07:10

gaohomway


For anyone looking for a reusable copy-paste solution,

Create a new file LinkText.kt and copy-paste this code,

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)
            }
        }
    }
}

Usage

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,
        ),
)

Screenshot,

Screenshot

Note

  1. I am handling web pages manually using a composable. Use UriHandler or other alternatives if manual control is not required.
  2. Style clickable and other text as required in LinkText.
like image 14
Abhimanyu Avatar answered Oct 22 '22 09:10

Abhimanyu


You can use https://github.com/firefinchdev/linkify-text

Its a single file, you can directly copy it to your project.

Also, it uses Android's Linkify for link detection, which is same as that of TextView's autoLink.

like image 7
Anirudh Gupta Avatar answered Oct 22 '22 09:10

Anirudh Gupta