Android

Android SpannableStringBuilder 用法

Android SpannableStringBuilder 用法

SpannableStringBuilder,和String一樣都是一種字串型別。不同的是SpannableString可以通過使用其方法setSpan方法實現字串各種文字的樣式。例如:段落文字、底線、文字放大、背景顏色、文字顏色、圖片塞入文字、刪除線、毛玻璃、段落圓點、粗體斜體、超連結、行數高度

文章目錄

  1. 段落文字
  2. 底線放大
  3. 背景顏色文字顏色
  4. 點擊圖標
  5. 段落圖刪除線
  6. 段落圓點修飾
  7. 粗體斜體
  8. 超連結行數高度
  9. 畫面布局
  10. 程式範例
  11. 效果展示
  12. Github

1.段落文字

val leadingMarginText = findViewById<TextView>(R.id.leadingMarginText)

val one = "1.此APP所發送之驗證碼,將在您索取3分鐘後失效。\n2.無法收到驗證碼嗎?\n"
val spannableStringBuilder = SpannableStringBuilder(one)
//計算要空多少格
val oneMeasure = leadingMarginText.paint.measureText("1.").toInt()
//設定段落的文字長度
spannableStringBuilder.setSpan(
    LeadingMarginSpan.Standard(0, oneMeasure),
    0,
    spannableStringBuilder.length,
    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)

val two = "a.手機設定是否有阻攔廣告訊息\nb.訊息容量已滿、訊號是否正常\nc.若以上皆正常仍無法收到簡訊請先與您的電信公司確認,以免重複發送多次簡訊驗 證碼導致帳號無法使用。"
spannableStringBuilder.append(two)
//計算要空多少格
val twoMeasure = leadingMarginText.paint.measureText("1.a.").toInt()
//設定段落的文字長度
spannableStringBuilder.setSpan(
    LeadingMarginSpan.Standard(
        oneMeasure,
        twoMeasure
    ), one.length, spannableStringBuilder.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
)

leadingMarginText.text = spannableStringBuilder

2.底線放大

val underlineSizeText = findViewById<TextView>(R.id.underlineSizeText)
val spannableStringBuilder = SpannableStringBuilder("文字的下底線和放大和縮小範例")
//下底線
spannableStringBuilder.setSpan(UnderlineSpan(), 4,6, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
//放大
spannableStringBuilder.setSpan(RelativeSizeSpan(1.5f), 7,9, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
//縮小
spannableStringBuilder.setSpan(RelativeSizeSpan(0.5f), 10,12, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)

underlineSizeText.text = spannableStringBuilder

3.背景顏色文字顏色

val backgroundForegroundText = findViewById<TextView>(R.id.backgroundForegroundText)
val spannableStringBuilder = SpannableStringBuilder("文字的背景顏色和文字顏色範例")
//背景顏色
spannableStringBuilder.setSpan(BackgroundColorSpan(Color.RED), 3,7, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
//文字顏色
spannableStringBuilder.setSpan(ForegroundColorSpan(Color.BLUE), 8,12, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)

backgroundForegroundText.text = spannableStringBuilder

4.點擊圖標

val clickImageText = findViewById<TextView>(R.id.clickImageText)
val spannableStringBuilder = SpannableStringBuilder("文字的部分點擊和圖標範例")
//背景顏色
spannableStringBuilder.setSpan(object : ClickableSpan() {
    override fun onClick(view: View) {
        Toast.makeText(this@MainActivity, "點擊了", Toast.LENGTH_SHORT).show()
    }
}, 3,7, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
//文字顏色(改藍色比較像可點擊的樣子)
spannableStringBuilder.setSpan(ForegroundColorSpan(Color.BLUE), 3,7, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
//圖標
val imageSpan = ImageSpan(this, R.drawable.apple)
spannableStringBuilder.setSpan(imageSpan, 8,10, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)

//設定才能點
clickImageText.movementMethod = LinkMovementMethod.getInstance()
clickImageText.text = spannableStringBuilder

5.段落圖刪除線

val drawableStrikeThroughText = findViewById<TextView>(R.id.drawableStrikeThroughText)
val spannableStringBuilder = SpannableStringBuilder("文字的首圖和刪除線範例")
//首圖
val apple = ContextCompat.getDrawable(this, R.drawable.apple)!!
spannableStringBuilder.setSpan(DrawableMarginSpan(apple), 0,1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
//刪除線
spannableStringBuilder.setSpan(StrikethroughSpan(), 6,9, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)

drawableStrikeThroughText.text = spannableStringBuilder

6.段落圓點修飾

val bulletMaskFilterText = findViewById<TextView>(R.id.bulletMaskFilterText)
val spannableStringBuilder = SpannableStringBuilder("文字的段落圓點和修飾模糊範例")
//段落圓點
val width = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5f, resources.displayMetrics).toInt()
val bulletSpan = BulletSpan(width, Color.RED)
spannableStringBuilder.setSpan(bulletSpan, 0,1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
//修飾模糊
val blurMaskFilterSpan = MaskFilterSpan(BlurMaskFilter(resources.displayMetrics.density * 2, BlurMaskFilter.Blur.NORMAL))
spannableStringBuilder.setSpan(blurMaskFilterSpan, 8,12, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)

bulletMaskFilterText.text = spannableStringBuilder

7.粗體斜體

val boldItalicText = findViewById<TextView>(R.id.boldItalicText)
val spannableStringBuilder = SpannableStringBuilder("文字的粗體和斜體範例")
//粗體
spannableStringBuilder.setSpan(StyleSpan(Typeface.BOLD), 3,5, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
//斜體
spannableStringBuilder.setSpan(StyleSpan(Typeface.ITALIC), 6,8, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
boldItalicText.text = spannableStringBuilder

8.超連結行數高度

val urlLineHeightText = findViewById<TextView>(R.id.urlLineHeightText)
val spannableStringBuilder = SpannableStringBuilder("文字的Google超連結和行數高度行數高度行數高度範例。")
//超連結
spannableStringBuilder.setSpan(URLSpan("https://www.google.com.tw/"), 3,12, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
//文字顏色(改藍色比較像可點擊的樣子)
spannableStringBuilder.setSpan(ForegroundColorSpan(Color.BLUE), 3,12, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
//行數高度
spannableStringBuilder.setSpan(LineHeightSpan.Standard(90), 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)

//設定才能點
urlLineHeightText.movementMethod = LinkMovementMethod.getInstance()
urlLineHeightText.text = spannableStringBuilder

9.畫面布局

<?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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/leadingMarginText"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="32dp"
        android:textColor="@color/black"
        android:textSize="16sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <View
        android:id="@+id/view"
        android:layout_width="wrap_content"
        android:layout_height="1dp"
        android:layout_marginTop="8dp"
        android:background="@color/black"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/leadingMarginText" />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/underlineSizeText"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="32dp"
        android:textColor="@color/black"
        android:textSize="16sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/view" />

    <View
        android:id="@+id/view2"
        android:layout_width="wrap_content"
        android:layout_height="1dp"
        android:layout_marginTop="8dp"
        android:background="@color/black"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/underlineSizeText" />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/backgroundForegroundText"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="32dp"
        android:textColor="@color/black"
        android:textSize="16sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/view2" />

    <View
        android:id="@+id/view3"
        android:layout_width="wrap_content"
        android:layout_height="1dp"
        android:layout_marginTop="8dp"
        android:background="@color/black"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/backgroundForegroundText" />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/clickImageText"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="32dp"
        android:textColor="@color/black"
        android:textSize="16sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/view3" />

    <View
        android:id="@+id/view4"
        android:layout_width="wrap_content"
        android:layout_height="1dp"
        android:layout_marginTop="8dp"
        android:background="@color/black"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/clickImageText" />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/drawableStrikeThroughText"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="32dp"
        android:textColor="@color/black"
        android:textSize="16sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/view4" />

    <View
        android:id="@+id/view5"
        android:layout_width="wrap_content"
        android:layout_height="1dp"
        android:layout_marginTop="8dp"
        android:background="@color/black"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/drawableStrikeThroughText" />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/bulletMaskFilterText"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="32dp"
        android:textColor="@color/black"
        android:textSize="16sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/view5" />

    <View
        android:id="@+id/view6"
        android:layout_width="wrap_content"
        android:layout_height="1dp"
        android:layout_marginTop="8dp"
        android:background="@color/black"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/bulletMaskFilterText" />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/boldItalicText"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="32dp"
        android:textColor="@color/black"
        android:textSize="16sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/view6" />

    <View
        android:id="@+id/view7"
        android:layout_width="wrap_content"
        android:layout_height="1dp"
        android:layout_marginTop="8dp"
        android:background="@color/black"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/boldItalicText" />

    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/urlLineHeightText"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="32dp"
        android:layout_marginTop="8dp"
        android:layout_marginEnd="32dp"
        android:textColor="@color/black"
        android:textSize="16sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/view7" />

</androidx.constraintlayout.widget.ConstraintLayout>

10.程式範例

package com.example.test

import android.annotation.SuppressLint
import android.graphics.*
import android.os.Bundle
import android.text.*
import android.text.method.LinkMovementMethod
import android.text.style.*
import android.util.TypedValue
import android.view.View
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat


class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        //段落文字
        leadingMargin()

        //底線放大
        underLineSize()

        //背景文字顏色
        backgroundForeground()

        //點擊圖標
        clickImageText()

        //段落圖刪除線
        drawableStrikeThrough()

        //段落圓點修飾
        bulletMaskFilter()

        //粗體斜體
        boldItalic()

        //超連結行數高度
        urlLineHeight()
    }

    @SuppressLint("NewApi")
    private fun urlLineHeight() {
        val urlLineHeightText = findViewById<TextView>(R.id.urlLineHeightText)
        val spannableStringBuilder = SpannableStringBuilder("文字的Google超連結和行數高度行數高度行數高度範例。")
        //超連結
        spannableStringBuilder.setSpan(URLSpan("https://www.google.com.tw/"), 3,12, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
        //文字顏色(改藍色比較像可點擊的樣子)
        spannableStringBuilder.setSpan(ForegroundColorSpan(Color.BLUE), 3,12, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
        //行數高度
        spannableStringBuilder.setSpan(LineHeightSpan.Standard(90), 0, 1, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)

        //設定才能點
        urlLineHeightText.movementMethod = LinkMovementMethod.getInstance()
        urlLineHeightText.text = spannableStringBuilder
    }

    private fun boldItalic() {
        val boldItalicText = findViewById<TextView>(R.id.boldItalicText)
        val spannableStringBuilder = SpannableStringBuilder("文字的粗體和斜體範例")
        //粗體
        spannableStringBuilder.setSpan(StyleSpan(Typeface.BOLD), 3,5, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
        //斜體
        spannableStringBuilder.setSpan(StyleSpan(Typeface.ITALIC), 6,8, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
        boldItalicText.text = spannableStringBuilder
    }

    private fun bulletMaskFilter() {
        val bulletMaskFilterText = findViewById<TextView>(R.id.bulletMaskFilterText)
        val spannableStringBuilder = SpannableStringBuilder("文字的段落圓點和修飾模糊範例")
        //段落圓點
        val width = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 5f, resources.displayMetrics).toInt()
        val bulletSpan = BulletSpan(width, Color.RED)
        spannableStringBuilder.setSpan(bulletSpan, 0,1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
        //修飾模糊
        val blurMaskFilterSpan = MaskFilterSpan(BlurMaskFilter(resources.displayMetrics.density * 2, BlurMaskFilter.Blur.NORMAL))
        spannableStringBuilder.setSpan(blurMaskFilterSpan, 8,12, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)

        bulletMaskFilterText.text = spannableStringBuilder
    }

    private fun drawableStrikeThrough() {
        val drawableStrikeThroughText = findViewById<TextView>(R.id.drawableStrikeThroughText)
        val spannableStringBuilder = SpannableStringBuilder("文字的首圖和刪除線範例")
        //首圖
        val apple = ContextCompat.getDrawable(this, R.drawable.apple)!!
        spannableStringBuilder.setSpan(DrawableMarginSpan(apple), 0,1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
        //刪除線
        spannableStringBuilder.setSpan(StrikethroughSpan(), 6,9, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)

        drawableStrikeThroughText.text = spannableStringBuilder
    }

    private fun clickImageText() {
        val clickImageText = findViewById<TextView>(R.id.clickImageText)
        val spannableStringBuilder = SpannableStringBuilder("文字的部分點擊和圖標範例")
        //背景顏色
        spannableStringBuilder.setSpan(object : ClickableSpan() {
            override fun onClick(view: View) {
                Toast.makeText(this@MainActivity, "點擊了", Toast.LENGTH_SHORT).show()
            }
        }, 3,7, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
        //文字顏色(改藍色比較像可點擊的樣子)
        spannableStringBuilder.setSpan(ForegroundColorSpan(Color.BLUE), 3,7, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
        //圖標
        val imageSpan = ImageSpan(this, R.drawable.apple)
        spannableStringBuilder.setSpan(imageSpan, 8,10, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)

        //設定才能點
        clickImageText.movementMethod = LinkMovementMethod.getInstance()
        clickImageText.text = spannableStringBuilder
    }

    private fun backgroundForeground() {
        val backgroundForegroundText = findViewById<TextView>(R.id.backgroundForegroundText)
        val spannableStringBuilder = SpannableStringBuilder("文字的背景顏色和文字顏色範例")
        //背景顏色
        spannableStringBuilder.setSpan(BackgroundColorSpan(Color.RED), 3,7, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
        //文字顏色
        spannableStringBuilder.setSpan(ForegroundColorSpan(Color.BLUE), 8,12, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)

        backgroundForegroundText.text = spannableStringBuilder
    }

    private fun underLineSize() {
        val underlineSizeText = findViewById<TextView>(R.id.underlineSizeText)
        val spannableStringBuilder = SpannableStringBuilder("文字的下底線和放大和縮小範例")
        //下底線
        spannableStringBuilder.setSpan(UnderlineSpan(), 4,6, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
        //放大
        spannableStringBuilder.setSpan(RelativeSizeSpan(1.5f), 7,9, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
        //縮小
        spannableStringBuilder.setSpan(RelativeSizeSpan(0.5f), 10,12, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)

        underlineSizeText.text = spannableStringBuilder
    }

    private fun leadingMargin() {
        val leadingMarginText = findViewById<TextView>(R.id.leadingMarginText)

        val one = "1.此APP所發送之驗證碼,將在您索取3分鐘後失效。\n2.無法收到驗證碼嗎?\n"
        val spannableStringBuilder = SpannableStringBuilder(one)
        //計算要空多少格
        val oneMeasure = leadingMarginText.paint.measureText("1.").toInt()
        //設定段落的文字長度
        spannableStringBuilder.setSpan(
            LeadingMarginSpan.Standard(0, oneMeasure),
            0,
            spannableStringBuilder.length,
            Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        )

        val two = "a.手機設定是否有阻攔廣告訊息\nb.訊息容量已滿、訊號是否正常\nc.若以上皆正常仍無法收到簡訊請先與您的電信公司確認,以免重複發送多次簡訊驗 證碼導致帳號無法使用。"
        spannableStringBuilder.append(two)
        //計算要空多少格
        val twoMeasure = leadingMarginText.paint.measureText("1.a.").toInt()
        //設定段落的文字長度
        spannableStringBuilder.setSpan(
            LeadingMarginSpan.Standard(
                oneMeasure,
                twoMeasure
            ), one.length, spannableStringBuilder.length, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        )

        leadingMarginText.text = spannableStringBuilder
    }
}

11.效果展示

12.Github

Android SpannableStringBuilder 用法 Github

發表迴響