🌎

Jetpack Compose Text内にLinkをつける

2024/05/11に公開

📕Overview

https://developer.android.com/develop/ui/compose/text/user-interactions#click-with-annotation

🌎Click with annotation

ユーザーがTextコンポーザブルをクリックしたとき、Text値の一部に追加情報を付けたい場合があります。例えば、ブラウザで開かれる特定の単語に付けられたURLのようなものです。こ の注釈は、 パ ラ メ タ と し て タ グ (String)、 ア イ テ ム (String)、 テ キ ス ト 範囲を取 り ます。AnnotatedStringから、これらのアノテーションをタグやテキスト範囲でフィルタリングすることができます。以下はその例です:]

[Example]

@Composable
fun AnnotatedClickableText() {
    val annotatedText = buildAnnotatedString {
        append("Click ")

        // We attach this *URL* annotation to the following content
        // until `pop()` is called
        pushStringAnnotation(
            tag = "URL", annotation = "https://developer.android.com"
        )
        withStyle(
            style = SpanStyle(
                color = Color.Blue, fontWeight = FontWeight.Bold
            )
        ) {
            append("here")
        }

        pop()
    }

    ClickableText(text = annotatedText, onClick = { offset ->
        // We check if there is an *URL* annotation attached to the text
        // at the clicked position
        annotatedText.getStringAnnotations(
            tag = "URL", start = offset, end = offset
        ).firstOrNull()?.let { annotation ->
            // If yes, we log its value
            Log.d("Clicked URL", annotation.item)
        }
    })
}

🧷summary

WebPageへのリンクをテキスト内に含めるのはよく見かける例です。今回はdeveloper.android.comの情報が記載されているWebページへリンクを貼ってみましょう。コードを少し修正しないと機能を実装できませんでした。

ClickableTextのonClickイベント内で、URLを開くためのコードが不足しているため、クリックしてもサイトに飛ばない状態になっています。

以下のように、Intentを使用してブラウザを開くコードを追加します。ただし、このコードは@Composable関数の外部で実行する必要があります。@Composable関数内から直接外部アクティビティを開始することはできません。
[全体のコード]

package com.junichi.textlink

import android.content.Intent
import android.net.Uri
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.withStyle
import androidx.compose.ui.tooling.preview.Preview
import com.junichi.textlink.ui.theme.TextLinkTheme

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            TextLinkTheme {
                // A surface container using the 'background' color from the theme
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    LinkTextScreen()
                }
            }
        }
    }
}

@Composable
fun LinkTextScreen() {
    val annotatedText = buildAnnotatedString {
        append("Click ")

        pushStringAnnotation(
            tag = "URL", annotation = "https://developer.android.com"
        )
        withStyle(
            style = SpanStyle(
                color = Color.Blue, fontWeight = FontWeight.Bold
            )
        ) {
            append("here")
        }

        pop()
    }

    val context = LocalContext.current

    ClickableText(text = annotatedText, onClick = { offset ->
        annotatedText.getStringAnnotations(
            tag = "URL", start = offset, end = offset
        ).firstOrNull()?.let { annotation ->
            val intent = Intent(Intent.ACTION_VIEW, Uri.parse(annotation.item))
            context.startActivity(intent)
        }
    })
}

このコードでは、LocalContext.currentを使用して現在のContextを取得し、そのContextを使用してIntentを開始します。Intent.ACTION_VIEWアクションとURLを指定して新しいIntentを作成し、startActivityメソッドを使用してそのIntentを開始します。これにより、指定したURLがブラウザで開かれます。

[実行結果]
Google Accountへのログインを求めれることがあるかもしれないですが、 Skipすることもできます。

🧑‍🎓thoughts

いかがでしたでしょうか。今回はアプリからWebページへのリンクをクリックすると表示することができる機能を実装しました。Flutterだとライブラリが必要ですが、ネイティブは標準機能でついてるので便利でしたね。これって、Flutterから呼べないのかな?

Discussion