0
0

More than 1 year has passed since last update.

Spannable風のTextを作る

text_1.png

この画像の年月日の部分は、1つのTextに対して、複数のスタイルを適用する(Spannable風)ことで実現しています。
以下を参考にしています。

一部のフォントサイズを小さくする

    val smallSpanStyle = SpanStyle(fontSize = MaterialTheme.typography.bodySmall.fontSize)
    Text(
        text = buildAnnotatedString {
            append(day.format("yy"))
            withStyle(style = smallSpanStyle) {
                append("年")
            }
            append(day.format("M"))
            withStyle(style = smallSpanStyle) {
                append("月")
            }
            append(day.format("d"))
            withStyle(style = smallSpanStyle) {
                append("日")
            }
        },
        style = MaterialTheme.typography.titleLarge,
        modifier = modifier,
    )

Spannable風のテキストを作るには、buildAnnotatedStringを使用します。
基本のスタイルは、Textのstyleに「MaterialTheme.typography.titleLarge」を指定し、
年月日の部分は、SpanStyle(fontSize = 小さめ)を指定しています。

応用

私の場合、Spannableを業務利用するのでよく使うのが、
利用規約プライバシーポリシーに同意...」ですかね。
後は、電話番号で電話かけたりとかもしますが、ともかく

  • アンダーラインを引いて
  • クリックイベントを拾って
  • なんかする

が一番多いですので、それを作ってみます。

自作LinkTextコンポーネント

先ほどとは違い、Styleと文字を順に追加していくのではなく、
文字全部を表示して、後からこの文字を装飾したい!みたいな使い方を想定しています。
GitHubのソースはこちら

LinkText.kt
@OptIn(ExperimentalTextApi::class)
@Composable
fun LinkText(
    text: String,
    linkTextList: List<LinkTextData>,
    modifier: Modifier = Modifier,
    onClick: (url: String) -> Unit,
) {
    // ①文字装飾定義
    val linkSpanStyle = SpanStyle(
        color = Color.Blue,
        textDecoration = TextDecoration.Underline,
    )

    // ②装飾文字生成
    val annotatedText = buildAnnotatedString {
        append(text)
        linkTextList.forEach { linkData ->
            val start = text.indexOf(linkData.label)
            if (start >= 0) {
                val end = start + linkData.label.length
                // ③UrlAnnotationを指定
                addUrlAnnotation(UrlAnnotation(linkData.url), start, end)
                // ④同じ場所に①の文字装飾を指定
                addStyle(linkSpanStyle, start, end)
            }
        }
    }

    ClickableText(
        text = annotatedText,
        modifier = modifier,
        onClick = { pos ->
            // ⑤タップ位置からUrlAnnotaionを取得
            annotatedText.getUrlAnnotations(start = pos, end = pos).firstOrNull()?.let { range ->
                onClick.invoke(range.item.url)
            }
        },
    )
}

data class LinkTextData(val label: String, val url: String)

annotationとして、文字装飾用とクリックイベント用の2種類のannotationが必要です。
リンクにしたい文字の位置をstart, endで取得して、①文字装飾と③UrlAnnotationを指定します。
クリックが発火したときに、発火位置から③で生成したUrlAnnotationを取得して、とれればイベントを発火させています。

使う側

こんな感じで使います。
リンクを押すと、uriHandlerを経由して外部ブラウザで各URLが開きます。

val uriHandler = LocalUriHandler.current
LinkText(
    text = "GoogleもしくはYahooを開きます。",
    linkTextList = listOf(
        LinkTextData("Google", "https://www.google.co.jp/"),
        LinkTextData("Yahoo", "https://www.yahoo.co.jp"),
    ),
    modifier = Modifier.padding(8.dp),
    onClick = { url ->
        uriHandler.openUri(url)
    },
)

linktext_1.png

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0