Android TextView实现一串文字特定几个字改变颜色
遇到一个需求,让Android端实现给定一个字符串指定下标的几个字颜色与其他字颜色不一致。
主要是用ForegroundColorSpan这个API来传入颜色值,用SpannableString来设置指定索引下标的字的颜色值。
这里通过给定一个输入文字描述框,要求输入指定下标,输入格式类似于1,3,4,6。输入数字,同时用英文逗号隔开,只要点击按钮提交以后,则来改变显示的字体颜色。
布局文件如下所示:
<?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">
<EditText
android:id="@+id/edit_num"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="输入下标(例如 1,3,5)"
tools:ignore="MissingConstraints">
</EditText>
<Button
android:id="@+id/submit"
android:text="提交"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.467"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/edit_num"
app:layout_constraintVertical_bias="0.26"
tools:ignore="MissingConstraints">
</Button>
<TextView
android:id="@+id/text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="HelloWorld"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
MainActivity如下所示:
import android.graphics.Color
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.text.Spannable
import android.text.SpannableString
import android.text.style.ForegroundColorSpan
import android.widget.Button
import android.widget.EditText
import android.widget.TextView
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val indexInput = findViewById<EditText>(R.id.edit_num)
val textView = findViewById<TextView>(R.id.text_view)
val buttonSubmit = findViewById<Button>(R.id.submit)
val originalText = "HelloWorld 测试文字颜色变化"
// 初始化显示原始文本
textView.text = originalText
buttonSubmit.setOnClickListener {
val s = indexInput.text
val strArray = s?.toString()
val indexArray = strArray?.split(",")
val len = indexArray?.size ?: 20
val indexNumArray = IntArray(len){0}
// 创建 SpannableString
val spannableString = SpannableString(originalText)
indexArray?.let {
for (i in indexArray.indices) {
indexNumArray[i] = Integer.parseInt(indexArray[i])
val idx = indexNumArray[i]
// 检查下标是否有效,避免越界错误
if (idx in originalText.indices) {
// 为每个字符创建一个新的 ForegroundColorSpan
val redColorSpan = ForegroundColorSpan(Color.RED)
spannableString.setSpan(
redColorSpan,
idx,
idx + 1,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
}
}
}
textView.text = spannableString
}
}
}
其中这里有个需要注意的是:
如果需要多次变更字体颜色,需要把ForegroundColorSpan设置在循环体内。如果把ForegroundColorSpan放在循环体外,则只有最后一个字会生效。错误示例如下:
val redColorSpan = ForegroundColorSpan(Color.RED)
indexArray?.let {
for (i in indexArray.indices) {
indexNumArray[i] = Integer.parseInt(indexArray[i])
val idx = indexNumArray[i]
spannableString.setSpan(redColorSpan, idx, idx + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) // 第index个字
}
}
问题原因:
- 你在循环中对每个字符的位置都应用了相同的
ForegroundColorSpan
(红色),并且每次调用setSpan
时,该Span
会替换之前的位置上的Span
,导致只有最后一个字符保持红色。
解决方案:
为了解决这个问题,确保为每个字符应用不同的颜色时不要覆盖先前的 Span
。可以通过创建一个新的 ForegroundColorSpan
实例,并在每次 setSpan
时分别应用。
关键更改:
- 确保每次
setSpan
使用新的ForegroundColorSpan
对象:- 在每次循环中创建一个新的
ForegroundColorSpan
实例,确保不会覆盖先前的颜色设置。
- 在每次循环中创建一个新的
- 下标校验:
- 在
setSpan
之前,确保你所提供的下标值是合法的,避免越界问题(idx in 0 until originalText.length
)。
- 在
- IntArray 初始化:
- 使用
IntArray
来存储转换后的下标值,并确保在处理时进行合理的范围检查。
- 使用
效果:
当用户输入下标(例如 1,3,5
),然后点击按钮时,指定下标的字符将正确变为红色,不会再出现只有最后一个字符变色的情况。