我们在 res/values 目录中的 xml 文件中作为资源保存的 String 值可以使用 android.text.Spannable 接口中定义的方法进行样式设置,android.text.Spanned 接口中定义的字段(Spannable是 Spanned 的子接口),具有 android.text.SpannableStringBuilder 类(SpannableStringBuilder 是 Spannable 的子类)和 android.text.style 包中定义的类,特别是 android.text.style.ForegroundColorSpan 和 android.text。 style.StyleSpan 类。
由于 SpannableStringBuilder 是 Spannable 的子类,而 Spannable 是 Spanned 的子接口。 Spannable 接口的所有字段和 Spannable 接口的方法的完整实现都存在于 SpannableStringBuilder 类中。
1.现在要设置文本资源的样式,我们必须首先使用资源对象的方法 getString(int resource_id) 方法获取它的资源 ID,以获取与我们称为 getResources() 的 Context 子类关联的资源对象方法。例如-
字符串标题 = getResources().getString(R.string.string_title);
2.现在我们需要创建一个 SpannableStringBuilder 对象及其公共无参数构造函数或任何其他公共构造函数。
3.SpannableStringBuilder 的工作方式有点复杂。我们可以使用其中一种 append 方法将文本附加到 SpannableStringBuilder。SpannableStringBuilder 持有的文本的某些部分可以标记为跨区,并且只有该部分被设置样式(带有颜色,字体等),当在 SpannableStringBuilder 对象的任何索引处附加新文本时,如果新文本也将被标记为跨区,则由 Spanned 接口的字段定义。
我们调用由 Spannable 接口声明并由 SpannableStringBuilder 类实现的 setSpan(Object what, int start, int end, int flags) 方法。
这里what是android.text.style.StyleSpan包的一个类的一个对象,这个对象定义了我们要应用的样式的类型,flags是Spanned接口中定义的字段之一,这个flags参数指定当一个新文本将附加在此 SpannableStringBuilder 对象中的任何索引处,如果新文本也将被标记为跨区。
4.android.text.style.StyleSpan包的不同类定义了不同的样式,但目前我只知道android.text.style.StyleSpan类的一种用法和android.text的一种用法。 style.ForegroundColorSpan 类。
StyleSpan 类的一个公共构造函数采用一个 int 值,该值可能是 android.graphics.Typeface 类(BOLD,BOLD_ITALIC,ITALIC,NORMAL)中定义的字段之一的值。正如这些字段名称所暗示的那样,它们将为文本设置样式作为粗体或粗体斜体或斜体或正常。
例如-
StyleSpan style_span=new StyleSpan(Typeface.ITALIC);
ForegroundColorSpan 类的一个公共构造函数采用一个 int 值,该值可以是任何十六进制 argb 颜色值,也可以是 android.graphics.Color 类中定义的字段之一的值(BLACK、BLUE、CYAN、DKGRAY、灰色,绿色,LTGRAY,洋红色,红色,透明,白色,黄色)。因为这些字段的名称表明它们为文本着色。要将 argb 十六进制颜色值作为 int 提供给构造函数,我们以 0X 或0x,java 然后自动将十六进制值转换为其等效的 int。
例如-
ForegroundColorSpan fore_ground_color_Span = new ForegroundColorSpan(Color.RED);
5.在setSpan(Object what, int start, int end, int flags)方法中,
start 和 end 定义要标记为 spanned 的文本的初始范围,start 指定起始字符的索引,end 指定传递结束字符的字符的索引,例如 -
对于字符串“01234”
new SpannableStringBuilder("01234").setSpan(foreground_color_span, 1, 3, span_flag);
最初只会将 12 标记为跨区,因此起始字符是起始索引指定的“1”,结束字符是索引指定的 2 (end-1)。
因此,如果我们为 start 和 end 赋予相同的值,那么最初将不会覆盖文本并且范围将为零,对于上面的示例,如果 start 和 end 都为 1,则起始字符为 1,但结束字符为0.这没有意义,在这种情况下,最初不会有文本标记为跨越。
6. 现在,Spanned 接口的某些字段的工作方式如下所述。
当添加新文本时............
SPAN_EXCLUSIVE_EXCLUSIVE 不将附加在起始字符之前和结束字符之后的任何文本标记为跨区,但将附加在起始字符之后或结束字符之前的任何文本标记为跨区,SPAN_EXCLUSIVE_EXCLUSIVE 是 SPAN_POINT_MARK 的同义词,如果范围为零表示最初没有将文本标记为跨区,然后在任何索引处附加的任何文本都不会使用此标志标记为跨区,
根据我的测试,SPAN_EXCLUSIVE_INCLUSIVE 不会将在起始字符之前附加的任何文本标记为跨区,而是将在起始字符之后附加的任何文本或在结束字符之前或之后附加的任何文本标记为跨区,根据我的测试 SPAN_EXCLUSIVE_INCLUSIVE 是 SPAN_POINT_POINT 的同义词,如果范围为零,这意味着最初没有文本被标记为跨越,那么任何附加在任何索引处的文本都不会被这个标志标记为跨越,
根据我的测试,SPAN_INCLUSIVE_EXCLUSIVE 不会将附加在结束字符之前的任何文本标记为跨越,而是将附加在起始字符之后或之前的任何文本或附加在结束字符之后的任何文本标记为跨越,根据我的测试 SPAN_INCLUSIVE_EXCLUSIVE 是 SPAN_MARK_MARK 的同义词,如果范围为零,这意味着最初没有文本被标记为跨越,那么任何附加在任何索引处的文本都不会被这个标志标记为跨越,
SPAN_INCLUSIVE_INCLUSIVE 将在开始和结束字符之后或之前附加的任何文本标记为跨区,SPAN_INCLUSIVE_INCLUSIVE 是 SPAN_MARK_POINT 的同义词,如果范围为零,则意味着最初没有文本被标记为跨区,然后在任何索引处附加的任何文本在开始或结束字符之后或之前始终使用此标志标记为跨越,
我无法正确理解 Spanned 接口中定义的其他标志常量。
7.现在我们可以在 SpannableStringBuilder 对象中附加任何文本,例如使用一种附加方法或一种插入方法-
SpannableStringBuilder text = new SpannableStringBuilder();
……
text.append(string_value); //append(CharSequence text) 将给定的字符串追加到末尾
//insert(int where, CharSequence tb) 在索引为 (where-1) 的字符之后插入给定的字符串
插入到这个 SpannableStringBuilder 的新文本是否会被标记为“spanned and styled”,这取决于我们为上一次调用 setSpan(Object what, int start, int end, int flags) 指定的标志方法。
8.如果我们想对 SpannableStringBuilder 对象中的文本应用多个样式,那么我们可以再次调用 setSpan(Object what, int start, int end, int flags) 方法并将样式指定给 what参数,以及最初设置样式的开始和结束,然后是标志参数,用于指定插入任何新文本时样式的行为方式。
这里重要的是,当我们调用 span 方法时,我们指定的样式与 start、end 和 flag 配对,这与之前对 span 方法的任何调用无关,这意味着对 span 方法的每次调用都会应用一个样式独立于也应用于相同文本的任何其他样式。即使我们再次应用相同的样式但对“what”参数使用新对象,此样式也独立于先前应用的相同样式工作,唯一的方法是修改一个已经应用的样式是为“what”参数指定相同的对象。
例如-
TextView txv=(TextView)findViewById(R.id.textView9);
String myString = "01234";
Object ob;
int start = 1;
int end = 4;
int spanFlag = Spannable.SPAN_INCLUSIVE_INCLUSIVE;
SpannableStringBuilder spannableString = new SpannableStringBuilder(myString);
ForegroundColorSpan foregroundSpan = new ForegroundColorSpan(Color.RED);
ob=foregroundSpan;
spannableString.setSpan(foregroundSpan, start, end, spanFlag);
txv.setText(spannableString);
这将导致 txv 有“01234”,其中“123”部分是红色的。
end=3;
spanFlag = Spannable.SPAN_EXCLUSIVE_INCLUSIVE;
spannableString.setSpan(new StyleSpan(Typeface.BOLD), start, end, spanFlag);
txv.setText(spannableString);
这将导致 txv 有“01234”,其中“123”部分是红色,子部分“12”既是粗体(脂肪)又是红色,但“3”只是红色而不是粗体,因为“结束”值。
spannableString.insert(end, "x");
spannableString.insert(start, "x");
txv.setText(spannableString);
这将导致 txv 具有“0x12x34”,其中“x12x3”部分为红色,只有“12x3”为粗体,
发生这种情况是因为红色样式最初应用于带有 SPAN_INCLUSIVE_INCLUSIVE 标志的文本“123”,这意味着在 1 和 3 之后或之前附加的任何文本始终包含在红色样式区域中,因此“x12x3”为红色
粗体样式最初应用于带有 SPAN_EXCLUSIVE_INCLUSIVE 标志的文本“12”,这意味着在 1 之前附加的任何文本都不包含在样式区域中,但在 1 之后和之后或之前附加的任何文本始终包含在粗体样式区域中,因此只有“12x3”是粗体。
int end=3;
spanFlag = Spannable.SPAN_EXCLUSIVE_EXCLUSIVE;
foregroundSpan = new ForegroundColorSpan(Color.RED);
spannableString.setSpan(foregroundSpan, start, end, spanFlag);
txv.setText(spannableString);
这将在文本“x1”上产生另一个独立的红色样式,但由于已经对文本“x12x3”应用了另一个红色样式,我们将看到 txv 没有区别。
要真正改变之前应用的红色样式,我们需要传递与第一次调用 setSpan 方法时相同的 ForegroundColorSpan 对象,我们应该使用以下代码而不是上面的代码,
int end=3;
spanFlag = Spannable.SPAN_EXCLUSIVE_EXCLUSIVE;
spannableString.setSpan(ob, start, end, spanFlag); //ob refers to the same the styling object as we specified on the first occasion
txv.setText(spannableString);
这将导致只有“x1”是红色的,而且如果我们在“1”之后或“x”之前附加任何文本,那么该文本将不会包含在红色样式区域中,而是“x”之后的任何文本并且在“1”之前总是包含在红色样式区域中,因为我们这次指定了 SPAN_EXCLUSIVE_EXCLUSIVE 标志。
9.最后一件事,如果我们想完全删除一个样式及其跨度标志规则,那么我们可以调用由 SpannableStringBuilder 类实现的 Spannable 接口定义的 removeSpan(Object what) 方法,我们需要将相同的样式对象传递给removeSpan 方法,就像我们给 setSpan 方法一样。
感谢以上答案,特别是颜色示例对我正确理解这些标志有很大帮助。