テキストを装飾する -SpannableString-

# SpannableStringとは？

### 概要
・文字列にさまざまな装飾やスタイル設定を行えるようにするためのもの
・一つの文字列に対して複数のスタイルを適用させることができる

## 注意点
・例として掲載しているコードはViewBindingを使用しています
　（変数`b`として定義しています）


## 主な種類

|クラス名|概要|
|:-:|:-:|
|ForgroundColorSpan|テキストの色を変更する|
|BackgroundColorSpan   |テキストの背景色を変更する   |
|StyleSpan|テキストを太文字やイタリックにする   |
|TypefaceSpan   |指定フォントを適用する   |
|RelativeSizeSpan   |テキストのサイズを相対的に変更する   |
|AbsoluteSizeSpan   |テキストのサイズを絶対値で変更する   |
|URLSpan   |テキストをリンクにする   |
|UnderlineSpan   |テキストに下線を引く   |
|StrikethroughSpan   |テキストに取り消し線を引く   |
|ImageSpan   |テキストを指定した文字に置き換える   |

・他にもクリック可能にして、任意のアクションを実行する`ClickableSpan`や、インデントを設定する`LeadingMarginSpan`、箇条書きの丸を追加する`BulletSpan`などもあります
## 使い方

どのSpanクラスを使用するにしても、スタイルを適用する流れは共通です
1. SpannnableStringに文字列を渡す
1. SpannableString.setSpanを使用して文字列に装飾する
1. TextViewなどに文字列を渡す

例: 文字列に下線を引きたい場合

```kotlin
fun underLineSpan(){
    val sampleText = "このテキストに下線を引きます"
    //textをSpannableStringにする
    val spannableString = SpannableString(sampleText)
    //setSpanメソッドを使用してスタイルを設定
    spannableString.setSpan(
        UnderlineSpan(),
        0,
        sampleText.length,
        Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
    )
    //TextViewに文字列を渡す
    b.underLineText.text = spannableString
}
```

### `setSpan`について
・setSpanメソッドには4つの引数があります
1. 適用するSpanクラス
1. 適用を開始する文字のindex
1. 適用を終了する文字のindex
1. Span範囲の扱い方を指定するフラグ

・3の終了のインデックスに関しては指定したインデックス-1までの箇所に適用されます

例: 文字列が `HelloWorld`の場合

```kotlin
spannableString.setSpan(
    UnderlineSpan(), //使用するSpanクラス
    0, //開始のインデックス
    5, //終了のインデックス
    Spannable.SPAN_EXCLUSIVE_INCLUSIVE //終了のインデックスを含まない
)
```
とすると、

![Screenshot 2024-08-14 at 14.24.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3566351/62267f3a-2b10-f7cb-50d0-62f1a22699ac.png)

このように下線が引かれます

## サンプル
### ・`UnderlineSpan`
![Screenshot 2024-08-14 at 14.30.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3566351/f0ebf753-2b91-a157-0bc9-120d7d07a9f8.png)
```kotlin
 private fun setUnderSpan() {
        val sampleText = "ここが重要です"
        val spannableString = SpannableString(sampleText)
        spannableString.setSpan(
            UnderlineSpan(),
            0,
            sampleText.length,
            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
        )
        b.underLineText.text = spannableString
    }
```

### ・`ForegroundColorSpan`

![Screenshot 2024-08-14 at 14.32.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3566351/44c826b5-6a42-e1d3-8581-663ed2079643.png)
```kotlin
 private fun setForegroundColorSpan() {
        val sampleText = "Black and Red Text."
        val spannableString = SpannableString(sampleText)
        spannableString.setSpan(
            ForegroundColorSpan(Color.RED),
            sampleText.indexOf('R'),
            sampleText.length,
            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
        )
        b.textColorText.text = spannableString
    }
```

### ・`BackgroundColorSpan`
![Screenshot 2024-08-14 at 14.35.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3566351/e60752bd-766a-6423-0dea-4338c6df7590.png)
```kotlin
private fun setBackgroundSpan() {
        val sampleText = "change Background Color."
        val spannableString = SpannableString(sampleText)
        spannableString.setSpan(
            BackgroundColorSpan(Color.RED),
            sampleText.indexOf('B'),
            sampleText.length,
            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
        )
        b.backGroundColorText.text = spannableString
    }
```

### ・`StyleSpan`
![Screenshot 2024-08-14 at 14.36.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3566351/5a5ce97e-85ca-b639-bfc1-4bde094eb7cf.png)
```kotlin
fun setTextStyleSpan() {
        val sampleText = "通常の文字と太文字"
        val spannableString = SpannableString(sampleText)
        spannableString.setSpan(
            StyleSpan(Typeface.BOLD),
            sampleText.indexOf('太'),
            sampleText.length,
            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
        )
        b.styleText.text = spannableString
    }
```

### ・`RelativeSizeSpan`
![Screenshot 2024-08-14 at 14.36.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3566351/eb2d9a71-9ce8-aa63-4f20-24ec8607bba3.png)
```kotlin
fun setRelativeSizeSpan() {
        val sampleText = "normal text size and big size text."
        val spannableString = SpannableString(sampleText)
        spannableString.setSpan(
            RelativeSizeSpan(2f), //元の文字サイズの2倍
            sampleText.indexOf('b'),
            sampleText.length,
            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
        )
        b.relativeText.text = spannableString
    }
```

### ・`StrikethroughSpan`
![Screenshot 2024-08-14 at 14.36.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3566351/33fc49c8-16e3-47c0-e5e0-c16756c2ea74.png)
```kotlin  
fun setStrikethroughSpan() {
        val sampleText = "Original price $100"
        val spannableString = SpannableString(sampleText)
        spannableString.setSpan(
            StrikethroughSpan(),
            0,
            sampleText.length,
            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
        )
        b.strikeThroughText.text = spannableString
    }
```

### ・`ImageSpan`
![Screenshot 2024-08-14 at 14.42.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3566351/34646375-3996-7e2d-ae14-66c4ec25b52d.png)
```kotlin
fun setImageSpan() {
        val sampleText = "Hello,Android."
        val spannableString = SpannableString(sampleText)
        spannableString.setSpan(
            ImageSpan(this@MainActivity, android.R.drawable.sym_def_app_icon),
            sampleText.indexOf('A'),
            sampleText.length,
            Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
        )
        b.imageSpanText.text = spannableString
    }
```

## 余談
・一部のものに関してはSpannableStringを使用しない方法でも再現できます

### アンダーラインについて

・文字列があらかじめ決まっている場合、res/values/strings.xml内で

```xml
<resources>
    <string name="underline_text"><u>下線</u>を引きます</string>
</resources>
```
このように`<u>`タグで下線を引くことでできます

### URLリンクについて

TextViewで、

```xml
   <TextView
        android:autoLink="web"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
```
このように`autoLink`に`web`を指定することで自動的にURLをリンクにできます
また、`autoLink`は他にも電話番号やemailなどもリンクにできます

## 詳細・参照

・SpannableStringクラス
https://developer.android.com/reference/kotlin/android/text/SpannableString
・Spannableクラス
https://developer.android.com/reference/android/text/Spannable
・Android developers ガイド
https://developer.android.com/develop/ui/views/text-and-emoji/spans?hl=ja