Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

like image 734
Suragch Avatar asked Apr 07 '17 05:04

Suragch


People also ask

How can I span text in Android?

If you need to attach a small number of spans to a single text object, and the text itself is read-only, use SpannableString . If you need to modify text after creation, and you need to attach spans to the text, use SpannableStringBuilder .

What is a difference between Spannable and string?

A Spannable allows to attach formatting information like bold, italic, ... to sub-sequences ("spans", thus the name) of the characters. It can be used whenever you want to represent "rich text". The Html class provides an easy way to construct such text, for example: Html.

What is a Spannable?

Spannable is a Spanned , adding in the ability to modify the spans (to add or remove formatting), but not to modify the text itself. SpannedString is a concrete implementation of the Spanned interface. SpannableString is a concrete implementation of the Spannable interface.

How do you make Spannable strings?

Add a Bulleted list in Android. With the help of BulletSpan , you can create a bullet list in your application to display some information in a short and simple way. So, in this way, we can use Spans to style the texts present in our application.


1 Answers

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
like image 135
Suragch Avatar answered Oct 02 '22 04:10

Suragch