androidloopsspannablestringspanned

How to loop through the spans in a SpannedString or SpannableString in Android


If I have a SpannedString (or SpannableString) like this

enter image description here

SpannableString spannableString = new SpannableString("Hello World!");
ForegroundColorSpan foregroundSpan = new ForegroundColorSpan(Color.RED);
BackgroundColorSpan backgroundSpan = new BackgroundColorSpan(Color.YELLOW);
spannableString.setSpan(foregroundSpan, 1, 8, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
spannableString.setSpan(backgroundSpan, 3, spannableString.length() - 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
textView.setText(spannableString);

How would I loop through the spans of the resulting String?


Solution

  • Looping through the spans in order

    You can use getSpans to get an array of the spans in a Spanned or Spannable String. However, just looping through the getSpans results will not necessarily give them to you in order. To get them in order you can use nextSpanTransition.

    Here is an example with a SpannedString like the example in the question. (A SpannableString would work the same.) The green lines show where the span transitions are. The text is black by default.

    spannable string example

    The code finds the next span transition and then gets all the spans in the current range.

    int next;
    for (int i = 0; i < spannableString.length(); i = next) {
    
        // find the next span transition
        next = spannableString.nextSpanTransition(i, spannedString.length(), CharacterStyle.class);
    
        // get all spans in this range
        int numOfSpans = 0;
        CharacterStyle[] spans = spannableString.getSpans(i, next, CharacterStyle.class);
        for(int j = 0; j < spans.length; j++) {
            numOfSpans++;
        }
    
        Log.i("TAG", "spans from " + i + " to " + next + ": " + numOfSpans);
    }
    

    Output:

    spans from 0 to 1: 0
    spans from 1 to 3: 1
    spans from 3 to 8: 2
    spans from 8 to 11: 1
    spans from 11 to 12: 0
    

    Thanks to this code for ideas.

    Types of spans

    Normally when looping through the spans you would choose a certain type of span. For example, if you wanted to remove all the foreground color spans, you could do the following:

    // get spans
    ForegroundColorSpan[] spans = spannableString.getSpans(0, spannableString.length(), ForegroundColorSpan.class);
    
    // loop through spans
    for (ForegroundColorSpan span : spans) {
        spannableString.removeSpan(span);
    }
    

    Note that this wouldn't work with a SpannedString because the spans in a SpannedString are not mutable (see this answer).

    If you wanted to get all the spans of any type you would set the type as Object.class.

    Object[] spans = spannableString.getSpans(0, spannableString.length(), Object.class);
    

    If you wanted all the spans that affect the appearance at the character level, you would use CharacterStyle.class. If within the loop you wanted to further limit the spans to those belonging to MetricAffectingSpan, you could do it like this.

    CharacterStyle[] spans = spannableString.getSpans(0, spannableString.length(), CharacterStyle.class);
    for (CharacterStyle span : spans) {
        if (span instanceof MetricAffectingSpan) {
            // do something
        }
    }
    

    Here is a general hierarchical breakdown of the span types. It may not be complete. Read Spans, a Powerful Concept for more information.

    Object
        CharacterStyle
            BackgroundColorSpan
            ClickableSpan
                URLSpan
            ForegroundColorSpan
            MaskFilterSpan
            StrikethroughSpan
            SuggestionSpan
            UnderlineSpan 
            MetricAffectingSpan
                AbsoluteSizeSpan
                LocaleSpan
                RelativeSizeSpan
                ReplacementSpan
                    DynamicDrawableSpan
                        ImageSpan 
                ScaleXSpan
                StyleSpan
                SubscriptSpan
                SuperscriptSpan
                TextAppearanceSpan
                TypefaceSpan 
        ParagraphStyle
            AlignmentSpan
                AlignmentSpan.Standard
            BulletSpan
            DrawableMarginSpan
            IconMarginSpan
            LeadingMarginSpan
                LeadingMarginSpan.LeadingMarginSpan2
                LeadingMarginSpan.Standard
            LineBackgroundSpan
            LineHeightSpan
                LineHeightSpan.WithDensity
            QuoteSpan
            TabStopSpan
                TabStopSpan.Standard
            WrapTogetherSpan