setText(CharSequence text, BufferType type)
TextView.setText()
有两个重载方法用于设置文本内容:
java
//TextView.java
private BufferType mBufferType = BufferType.NORMAL;
//1
public final void setText(CharSequence text) {
setText(text, mBufferType); //mBufferType默认是BufferType.NORMAL类型
}
//2
public void setText(CharSequence text, BufferType type) {
.......
}
public enum BufferType {
NORMAL, SPANNABLE, EDITABLE
}
setText(CharSequence text)
最终也是调用了setText(CharSequence text, BufferType type)
方法将 text
设置为 TextView
的文本内容,并使用指定的 BufferType
类型来处理文本。
根据不同的类型,TextView
会采取不同的方式处理文本内容。两个参数的含义:
- text :
CharSequence
类型,可以是字符串或其他CharSequence
的实现类。 - type :文本的处理类型,类型为
BufferType
枚举,可选值为NORMAL
、SPANNABLE
或EDITABLE
。
BufferType
枚举类型包含以下三个常量:
NORMAL
:表示普通文本类型,不支持任何样式或效果,默认设置。SPANNABLE
:表示可使用插入Span
的文本类型,可以使用Spannable
或其子类(如SpannableString
、SpannableStringBuilder
)来设置文本的样式和效果,例如颜色、字体、点击事件等。EDITABLE
:表示可编辑的文本类型,用于EditTextView
中。
示例:
java
TextView textView = findViewById(R.id.textView);
// 1、使用默认的 BufferType.NORMAL 处理文本
textView.setText("Hello, World!");
// 2、使用 BufferType.SPANNABLE 处理文本,支持设置样式和效果
SpannableString spannableString = new SpannableString("Hello, World!");
spannableString.setSpan(new ForegroundColorSpan(Color.RED), 0, 5, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(spannableString, TextView.BufferType.SPANNABLE);
// 如果setText被以BufferType.SPANNABLE方式调用,那么文字可被转为 Spannable:
val spannableText = textView.text as Spannable
// 现在我们可以继续设置或删除 span
spannableText.setSpan(
ForegroundColorSpan(color),
0, 5, Spannable.SPAN_INCLUSIVE_INCLUSIVE)
// 3、使用 BufferType.EDITABLE 处理文本,支持编辑功能
Editable editable = Editable.Factory.getInstance().newEditable("Editable text");
textView.setText(editable, TextView.BufferType.EDITABLE);
下面主要介绍BufferType.SPANNABLE
在设置Span
时的一些优化建议。
官方推荐使用Span的最佳做法
已知SpannedString 实现的 Spanned接口,SpannableString、SpannableStringBuilder 实现的 Spannable接口,而他们又都继承了CharSequence接口,如下:
在 TextView
中设置文本时可以根据不同需求采用多种节省内存的方式。
1、不更改文本,只附加或分离 Span
TextView.setText()
包含能够以不同方式处理 Span
的多种重载。例如,可以使用以下代码设置 Spannable
文本对象:
kotlin
textView.setText(spannableObject)
调用此 setText()
重载时,TextView
会创建 Spannable
的副本作为 SpannedString
,并将其作为 CharSequence
保留在内存中。这意味着文本和 Span
都不可变,因此当需要更新文本或 Span
时,需要创建一个新的 Spannable
对象并再次调用 setText()
,而这也会触发重新测量和重新绘制布局。
如需表示这些 Span
可变,您可以改为使用 setText(CharSequence text, TextView.BufferType type)
,如下例所示:
kotlin
textView.setText(spannable, BufferType.SPANNABLE)
//将textView.text重新转变为Spannable
val spannableText = textView.text as Spannable
spannableText.setSpan(
ForegroundColorSpan(color),
8, spannableText.length,
SPAN_INCLUSIVE_INCLUSIVE
)
在该示例中,由于使用了 BufferType.SPANNABLE
参数,TextView
创建了 SpannableString
(注意这里不是SpannedString哟 ),而由 TextView
保留的 CharSequence
对象现在具有可变Span
标记和不可变文本。如需更新 Span
,我们可以将该文本作为 Spannable
进行检索,然后根据需要更新 Span
。
当附加、分离或重新定位 Span
时,TextView
会自动更新以反映对文本的更改。不过请注意:如果更改现有 Span
的内部属性,还需要调用 invalidate()
(如果进行与外观相关的更改)或 requestLayout()
(如果进行与测量相关的更改)。
2、在TextView 中多次设置文本
在某些情况下(例如使用 RecyclerView.ViewHolder
时),可能想要重复使用 TextView
并多次设置文本。默认情况下,无论是否设置 BufferType
,TextView
都会创建 CharSequence
对象的副本并将其保留在内存中,这意味着每次设置新的文本时,TextView
都会创建一个新对象。
如果希望更好地控制此过程并避免创建额外的对象,可以实现自己的 Spannable.Factory
并替换 newSpannable()
。可以不必创建新的文本对象,而直接对现有 CharSequence
进行类型转换并将其作为 Spannable
返回,如下所示:
csharp
//默认的Spannable.Factory
public static class Factory {
private static Spannable.Factory sInstance = new Spannable.Factory();
/**
* Returns the standard Spannable Factory.
*/
public static Spannable.Factory getInstance() {
return sInstance;
}
/**
* Returns a new SpannableString from the specified CharSequence.
* You can override this to provide a different kind of Spannable.
*/
public Spannable newSpannable(CharSequence source) {
return new SpannableString(source);
}
}
kotlin
//自定义Spannable.Factory
val spannableFactory = object : Spannable.Factory() {
override fun newSpannable(source: CharSequence?): Spannable {
return source as Spannable
}
}
请注意,在设置文本时,必须使用 textView.setText(spannableObject, BufferType.SPANNABLE)
。否则,源 CharSequence
将作为 Spanned
实例进行创建,并且无法转换为 Spannable
,从而导致 newSpannable()
抛出 ClassCastException
。
在替换 newSpannable()
之后,需要告知 TextView
使用新的 Factory
:
kotlin
textView.setSpannableFactory(spannableFactory)
请务必在获得对 TextView
的引用后立即设置 Spannable.Factory
对象。如果使用的是 RecyclerView
,请在首次创建视图时设置 Factory
对象。这可避免 RecyclerView
在将新的项绑定到 ViewHolder
时创建额外的对象。
3、更改内部 Span 属性
如果只需更改可变 Span
的内部属性(例如,自定义项目符号 Span
中的项目符号颜色),可以通过在创建 Span
时保留对该 Span
的引用来避免多次调用 setText()
所产生的开销。当需要修改 Span
时,可以修改引用,然后根据更改的属性类型,对 TextView
调用 invalidate()
或 requestLayout()
。
在下面的代码示例中,自定义项目符号实现的默认颜色为红色,在点击按钮时会变为灰色:
kotlin
class MainActivity : AppCompatActivity() {
// keeping the span as a field
val bulletSpan = BulletPointSpan(color = Color.RED)
override fun onCreate(savedInstanceState: Bundle?) {
...
val spannable = SpannableString("Text is spantastic")
// setting the span to the bulletSpan field
spannable.setSpan(
bulletSpan,
0, 4,
Spanned.SPAN_INCLUSIVE_INCLUSIVE
)
styledText.setText(spannable)
button.setOnClickListener {
// change the color of our mutable span
bulletSpan.color = Color.GRAY
// color won't be changed until invalidate is called
styledText.invalidate()
}
}
}
资料
【1】https://developer.android.com/guide/topics/text/spans?hl=zh-cn#best-practices