Android Spanned、SpannedString、Spannable、SpannableString和CharSequence Android中的文本处理涉及到多种类,包括Spanned、SpannedString、Spannable、SpannableString和CharSequence。这些类都与文本样式和格式有关,并且可以用于实现富文本显示和编辑。Spanned和SpannedString是只读的,而Spannable和SpannableString则是可编辑的。CharSequence是所有这些类的父接口。

113
Android提供了多种与文本和字符串相关的接口,包括:Spanned、SpannedString、Spannable、SpannableString和CharSequence。
通常在使用Html.fromHtml()将可链接文本显示在TextView中并应用一些样式后,我会在各种场景中使用上述所有接口。
我尝试从Android官方文档中理解这些接口的目的和用法,但失败了,因为它很令人困惑。这些接口的目的是什么?在哪些场景下最常用它们?在哪些情况下最好避免使用它们?使用它们时是否有明显的性能影响?
如果有人能提供一个合理的解释,将不胜感激。
2个回答

173
这些接口的目的是什么? 来自 yuml.me/edit/5f8da6cb 的图表 CharSequence 是一个标准的 Java 接口,表示一系列字符。 String 是最常用的 CharSequence 具体实现,其次是 StringBuilderSpanned 是一个带有“跨度”的 CharSequence,指示要应用于文本部分的格式,其中这些跨度不能被修改。 Spannable 是一个 Spanned,增加了修改跨度(添加或删除格式),但不能修改文本本身的能力。 SpannedStringSpanned 接口的一个具体实现。 SpannableStringSpannable 接口的一个具体实现。
在哪些场景中最常用它们?

当有一个方法返回一个值时(例如,在EditText上的getText()),或者当有一个方法以一个参数作为输入时(例如,在TextView上的setText())。

来自 yuml.me/edit/35c8f39f 的图表

在传统的Android开发中,你提到的使用Html.fromHtml()的情况可能是最常见的,因为带有SpannedTextViewWebView轻。然而,还有其他用例,例如:

在哪些情况下最好避免使用它们?

它们非常不适合用于解决脱发、除雪、热泵维修、制作舒芙蕾等问题。

:-)

在使用它们时是否存在明显的性能影响需要考虑?

接口本质上并没有"性能影响",它们只是API的描述。

我不知道SpannableString在任何特定操作上是否比SpannedString慢得多。但是,SpannableStringBuilder(允许对文本进行操作以及格式化该文本的跨度)在某些方面可能会比SpannableStringSpannedString慢一些。但是,性能差异是否足以成为问题将取决于使用情况。


4
因为官方文档没有提供相关信息,你是如何获取这些信息的?是通过手动浏览资料来源并辛苦查找吗? - Pacerier
11
@Pacerier:我不确定你在指称这个答案的哪个部分。例如,这个答案中所描述的类层次结构绝对来自文档,特别是JavaDocs。相反,这些东西对于对抗秃发的无用性则来自于个人观察。 - CommonsWare
@CommonsWare,这是否意味着在字符串中间带有表情符号的字符串将被视为spannableString而不是普通字符串.. 因为当my_string中有表情符号(例如watsapp中的表情符号)时,使用setText(my_string)会出现巨大的延迟,与只有纯文本的普通字符串相比较。 请给予一些指导...我曾经认为表情符号只是文本(Unicode)。 - eRaisedToX
1
@eRaisedToX:笑脸可以作为表情符号或图像实现。据我所知,表情符号将是单独的Unicode字符,并且可能会在常规字符串中出现。图像则需要“ImageSpan”,而“ImageSpan”又需要“SpannableString”。 - CommonsWare
大概是没错的...但是如果尝试处理表情符号并使用常规字符串,你将会回到对抗秃发的状态。当涉及到实现表情符号时,可编辑文本和SpannableStringBuilder是你最好的朋友。我发现,虽然它们只是Unicode,但是识别表情符号跨度和设置表情符号跨度的编码方式是影响性能成本的关键。如果不知道它们的功能,请小心使用其他人的表情符号库... - JamisonMan111

59

String

String是不可变的(即文本无法更改)。它也没有与之关联的任何范围(span)。范围是指跨越文本的区域,包括样式信息(如颜色、高亮、斜体、链接等)。因此,当您的文本不需要更改且不需要任何样式时,可以使用String

StringBuilder

StringBuilder具有可变文本,因此您可以在不创建新对象的情况下修改它。但是,它没有任何范围信息。它只是普通文本。因此,在需要更改文本但不关心其样式时,请使用StringBuilder

SpannedString

SpannedString具有不可变文本(就像String)和不可变范围信息。它是符合Spanned接口定义的具体实现。当您的文本具有样式但在创建后不需要更改文本或样式时,请使用SpannedString

注意:不存在SpannedStringBuilder,因为如果文本发生更改,则范围信息很可能也会发生更改。

SpannableString

SpannableString可以通过附加对象到特定部分,添加样式和其他元数据。它具有可变文本和可变范围信息。当您需要同时更改文本和样式时,请使用SpannableString

SpannableString是不可变的文本,但其跨度信息是可变的。它是实现Spannable接口所定义要求的具体实现。当您的文本不需要更改但样式需要更改时,请使用SpannableString

SpannableStringBuilder

SpannableStringBuilder既具有可变文本又具有跨度信息。它是实现SpannableEditable接口(以及其他接口)所定义要求的具体实现。当您需要更新文本及其样式时,请使用SpannableStringBuilder

CharSequence

CharSequence是一个接口而不是具体类。这意味着它只定义了任何实现它的类所遵循的一系列规则。上面提到的所有类都实现了它。因此,当您想要将对象类型泛化以获得最大的灵活性时,可以使用CharSequence。如果需要,您始终可以将其向下转换为StringSpannableStringBuilder等类型。


4
我很欣赏你回复的方式,非常易于阅读和理解,谢谢。 - JamisonMan111

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接