Spannable風のTextを作る
この画像の年月日の部分は、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のソースはこちら
@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)
},
)