Question

I'm trying to make a game to help kids learn math. I color code numbers so they can follow the logic. I also use textView.append() to build a long string to put into the activity.

Because I re-use the same numbers/symbols in instruction, I wanted a way to easily recycle the SpannableStrings, but it seems that the changes I make are applied correctly the first time, but thereafter sometimes work, sometimes partially work (e.g. right size/alignment, but wrong color), or don't work at all. I'm getting a mix of it all. I can't seem to find anyone experiencing a similar problem in Stackoverflow or anywhere else on Google.

Here's my code (simplified):

    // Declare static variables
    public static Spannable PLUS = new SpannableString("+ ");
    public static Spannable MINUS = new SpannableString("- ");
    public static Spannable TIMES = new SpannableString("X ");
    public static Spannable DIVIDED_BY = new SpannableString("/ ");
    public static Spannable EQUALS = new SpannableString("= ");

    // Function to color these strings.
    public void colorStrings() {
    makeBlack(PLUS);
    makeBlack(MINUS);
    makeBlack(TIMES);
    makeBlack(DIVIDED_BY);
    makeBlack(EQUALS);
    makeBlack(QMARK);
    }

    // Function that actually sets color, size, and alignment.
    public Spannable makeBlack(Spannable s) {s.setSpan(new ForegroundColorSpan(getResources().getColor(R.color.color_black)), 0, s.length()-1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    s.setSpan(new AbsoluteSizeSpan((int) getResources().getDimension(R.dimen.big_text)), 0, s.length()-1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    s.setSpan(new AlignmentSpan.Standard(Layout.Alignment.ALIGN_CENTER), 0, s.length()-1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    return s;
    }

    // Implementing above code by setting math1 TextView to give it colors.
    // Note that this is the exact code used for the first colored string in the
    // screen capture below.
    math1.setText(XX);
    math1.append(PLUS);
    math1.append(YY);
    math1.append(PLUS);
    math1.append(ZZ);
    math1.append(EQUALS);
    math1.append(QMARK);

(Note that I didn't provide the code for XX, YY, or ZZ...it's a bit more involved, since I have to get the data from a sharedpreference setting, but these experience essentially the same problem as the other math symbols/operators).

This is the resulting screen capture:

enter image description here

Perhaps the best example of my problem is the + (plus sign). At first, it's black as expected, but only 4 characters later (including spaces) it's the wrong color (blue, which it likely got from the previous SpannableString), though aligned and sized correctly. In the next colored string, it's displayed as expected (black, centered, large), but after that it's not formatted at all in the subsequent "colored" strings (small, left-aligned, green, like paragraph text). The code used looks exactly as above (i.e. math1.append(PLUS);), but the output is not consistently what I expected.

Last piece of information: Prior to globally declaring my variables, I locally declared them every time using the same makeBlack() and math1.append() functions, and it worked perfectly. I'm only getting this strange behavior after globally setting my variables.

Any suggestions on what's going wrong here?

Was it helpful?

Solution

You can't reuse the same span multiple times. Even if the span is exactly the same, you need to create a unique span for each section of the string you are wanting to change.

I am not sure where I read this, but I can confirm the answer.

Create a unique span for each thing you would like to change.

OTHER TIPS

It´s posible to use the same span multiple times, using

CharacterStyle.wrap(CharacterStyle c)

every time you need to use the same Span, pasing it as the parameter.

Just want to chime in since I encountered this same problem. If you dive into a Spannable class, such as SpannableStringBuilder, it becomes clear why you can't reuse a Span. When you set a span, this block is executed:

for (int i = 0; i < count; i++) {
    if (spans[i] == what) {
        int ostart = mSpanStarts[i];
        int oend = mSpanEnds[i];

        if (ostart > mGapStart)
            ostart -= mGapLength;
        if (oend > mGapStart)
            oend -= mGapLength;

        mSpanStarts[i] = start;
        mSpanEnds[i] = end;
        mSpanFlags[i] = flags;

        if (send) sendSpanChanged(what, ostart, oend, nstart, nend);

        return;
    }
}

Basically this code loops through the existing spans, compares to the span being set, and if it matches then updates the span bounds. The only way to prevent if (spans[i] == what) from being true is passing in a new instance of your span.

Span objects should not be reused. I had a similar problem before.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top