【问题标题】:Explain the meaning of Span flags like SPAN_EXCLUSIVE_EXCLUSIVE解释 SPAN_EXCLUSIVE_EXCLUSIVE 等 Span 标志的含义
【发布时间】:2012-04-10 09:01:36
【问题描述】:

有人可以通过示例清楚地解释span flags 中的每个SPAN_EXCLUSIVE_EXCLUSIVESPAN_MARK_MARK 的含义以及何时使用哪些标志?

官方文档说的时候我看不懂:

SPAN_EXCLUSIVE_EXCLUSIVE 类型的 Span 不会扩展为包含文本 插入到它们的起点或终点。

“扩展至包含”是指插入跨度后所做的编辑吗?

这是否意味着这些标志不会影响具有不可变文本的 Spannables?

【问题讨论】:

    标签: android


    【解决方案1】:

    标志不代表什么

    当我第一次看到Spannable 标志的INCLUSIVEEXCLUSIVE 部分时,我以为他们只是告诉了跨度是否包括跨度的开始和结束索引位置。这不是真的。让我用下面的例子来说明这一点。

    String myString = "01234";
    int start = 1;
    int end = 3;
    int spanFlag = Spannable.SPAN_INCLUSIVE_INCLUSIVE; // this is what is changing
    
    SpannableString spannableString = new SpannableString(myString);
    ForegroundColorSpan foregroundSpan = new ForegroundColorSpan(Color.RED);
    spannableString.setSpan(foregroundSpan, start, end, spanFlag);
    textView.setText(spannableString);
    

    结果如下:

    SPAN_INCLUSIVE_INCLUSIVE

    SPAN_INCLUSIVE_EXCLUSIVE

    SPAN_EXCLUSIVE_INCLUSIVE

    SPAN_EXCLUSIVE_EXCLUSIVE

    它们都是一样的!标志不影响跨度。 跨度总是在其起始索引处包含字符并排除在结束索引处的字符。

    标志的真正含义

    Spannable 标志的INCLUSIVEEXCLUSIVE 部分实际上告诉跨度是否应该包含在开始或结束位置插入的文本

    这里有一个修改过的例子来说明这一点。

    String myString = "01234";
    int start = 1;
    int end = 3;
    int spanFlag = Spannable.SPAN_INCLUSIVE_INCLUSIVE; // this is what is changing
    
    // set the span
    SpannableStringBuilder spannableString = new SpannableStringBuilder(myString);
    ForegroundColorSpan foregroundSpan = new ForegroundColorSpan(Color.RED);
    spannableString.setSpan(foregroundSpan, start, end, spanFlag);
    
    // insert the text after the span has already been set
    // (inserting at start index second so that end index doesn't get messed up)
    spannableString.insert(end, "x");
    spannableString.insert(start, "x");
    
    textView.setText(spannableString);
    

    以下是在末尾和开始索引处插入x 后的结果:

    SPAN_INCLUSIVE_INCLUSIVE

    SPAN_INCLUSIVE_EXCLUSIVE

    SPAN_EXCLUSIVE_INCLUSIVE

    SPAN_EXCLUSIVE_EXCLUSIVE

    注意事项

    • 在第二个示例中,我必须使用SpannableStringBuilder,因为SpannableString 中的文本是不可变的,因此您无法在其中插入文本。因此,对于SpannableString,这些标志通常没有意义。但是,可以想象这样一种情况:SpannableString 的跨度被复制到SpannableStringBuilderEditable,然后这些标志就有意义了。
    • See this answer 了解 SpannableStringSpannableStringBuilderEditable 等之间的区别。

    【讨论】:

    • 非常感谢您的说明性和完美答案。特别是对于这个非常好的观点:“跨度始终包含其起始索引处的字符并排除结束索引处的字符”。
    • 非常感谢!这应该是公认的答案
    【解决方案2】:

    “扩展至包含”是指插入跨度后所做的编辑吗?

    是的。例如,假设我们有以下内容:

    狐狸跳了起来。

    如果我们在粗体跨度上使用SPAN_EXCLUSIVE_EXCLUSIVE,并且我们在跨度中间插入文本,它仍然是粗体:

    棕色狐狸跳了起来。

    但是,如果我们在粗体跨度的开头或结尾插入文本,则插入的文本不是粗体:

    真正的狐狸精跳了起来。

    但是,如果我们使用了SPAN_INCLUSIVE_EXCLUSIVE,那么在开头插入的文本将作为 span 的一部分包含在内,我们将:

    真正敏捷的狐狸跳了起来。

    这是否意味着这些标志不会影响具有不可变文本的 Spannables?

    我会说它们对不可变文本的用途有限。大多数情况下,这些将与 SpannableStringBuilder 或在幕后使用的东西一起使用(例如,EditText)。

    【讨论】:

    【解决方案3】:

    我们在 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 方法一样。

    感谢以上答案,特别是颜色示例对我正确理解这些标志有很大帮助。

    【讨论】:

    • 对于看似很简单的问题,这似乎是一个非常详细的答案。我建议您编辑您的答案以专注于原始问题的细节。
    猜你喜欢
    • 1970-01-01
    • 2018-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-19
    • 2020-03-11
    相关资源
    最近更新 更多