[Android] SpannableStringBuilderを使って、文字列に色々色をつけてみた。そして、timerを使って着色位置を動かしてみた

# 1.概要
下の図の１行目のようにひとつの文字列の各文字の色を変えてみた。
また２行目のように文字列の文字を順次着色位置を移動させてみた(下のYouTubeのリンクで着色位置が移動する様子が見れます。)
。

![Screenshot_20241123_111840.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/501068/c3979b1f-652c-e93c-b355-2e9194bbae62.png)

https://youtube.com/shorts/1MK0mpMrhw4?si=NLoNB0zdDf9H0Zrw

# 2.参考にしたサイト
https://plugout.hateblo.jp/entry/2018/07/13/184519
https://qiita.com/atsuuu/items/48206ce6ed67d216ad55
http://ykonp.com/post-2226/


# 3.ソースコード
```kotlin
// https://plugout.hateblo.jp/entry/2018/07/13/184519
// https://qiita.com/atsuuu/items/48206ce6ed67d216ad55
// http://ykonp.com/post-2226/
package io.github.taka_guijiu.testtextcolor02


import android.graphics.Color
import android.os.Bundle
import android.text.Spannable
import android.text.SpannableStringBuilder
import android.text.style.ForegroundColorSpan
import android.widget.TextView
import androidx.activity.enableEdgeToEdge
import androidx.appcompat.app.AppCompatActivity
import androidx.core.os.HandlerCompat
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import kotlin.concurrent.timer

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        enableEdgeToEdge()
        setContentView(R.layout.activity_main)
        ViewCompat.setOnApplyWindowInsetsListener(findViewById(R.id.main)) { v, insets ->
            val systemBars = insets.getInsets(WindowInsetsCompat.Type.systemBars())
            v.setPadding(systemBars.left, systemBars.top, systemBars.right, systemBars.bottom)
            insets
        }

        val textView: TextView = findViewById(R.id.text_view1)
        val message: String = "abcdefgh"
        val spanColor0: Int = Color.RED
        val spanColor1: Int = Color.BLUE
        val spanColor2: Int = Color.BLACK
        val spanColor3: Int = Color.CYAN
        val spanColor4: Int = Color.GRAY
        val spanColor5: Int = Color.GREEN
        val spanColor6: Int = Color.YELLOW
        val spanColor7: Int = Color.MAGENTA

        SpannableStringBuilder(message).let {
            it.setSpan(ForegroundColorSpan(spanColor0), 0, 1, Spannable.SPAN_EXCLUSIVE_INCLUSIVE)
            it.setSpan(ForegroundColorSpan(spanColor1), 1, 2, Spannable.SPAN_EXCLUSIVE_INCLUSIVE)
            it.setSpan(ForegroundColorSpan(spanColor2), 2, 3, Spannable.SPAN_EXCLUSIVE_INCLUSIVE)
            it.setSpan(ForegroundColorSpan(spanColor3), 3, 4, Spannable.SPAN_EXCLUSIVE_INCLUSIVE)
            it.setSpan(ForegroundColorSpan(spanColor4), 4, 5, Spannable.SPAN_EXCLUSIVE_INCLUSIVE)
            it.setSpan(ForegroundColorSpan(spanColor5), 5, 6, Spannable.SPAN_EXCLUSIVE_INCLUSIVE)
            it.setSpan(ForegroundColorSpan(spanColor6), 6, 7, Spannable.SPAN_EXCLUSIVE_INCLUSIVE)
            it.setSpan(ForegroundColorSpan(spanColor7), 7, 8, Spannable.SPAN_EXCLUSIVE_INCLUSIVE)

            textView.text = it.subSequence(0, it.length)
        }

        var n = 0
        val message2 = "abcdefghijklmnopqrstuvwxyz"
        val l = message2.length

        timer(name = "curPos", period = 300L) {
            HandlerCompat.createAsync(mainLooper).post {
                n += 1
                val m = n % l
                SpannableStringBuilder(message2).let {
                    it.setSpan(
                        ForegroundColorSpan(spanColor0),
                        m,
                        m + 1,
                        Spannable.SPAN_EXCLUSIVE_INCLUSIVE
                    )
                        findViewById<TextView>(R.id.text_view2).text = it.subSequence(0, it.length)
                }


            }
        }
    }
}
```

```xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/text_view1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="180dp"
        android:text="Hello World!"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/text_view2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.502"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/text_view1" />

</androidx.constraintlayout.widget.ConstraintLayout>
```