Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

android: how to persistently store a Spanned?

I want to save a Spanned object persistently. (I'm saving the String it's based on persistently now, but it takes over 1 second to run Html.fromHtml() on it, noticeably slowing the UI.)

I see things like ParcelableSpan and SpannedString and SpannableString but I'm not sure which to use.

like image 331
Dan Jameson Avatar asked May 12 '12 18:05

Dan Jameson


3 Answers

My use case was about putting a Spanned into a Bundle, and Google brought me here. @CommonsWare is right that Parcelable is no good for persistent storage, but it's fine for storing into a Bundle. Most spans seems to extend ParcelableSpan, and so this worked for me in onSaveInstanceState:

ParcelableSpan spanObjects[] = mStringBuilder.getSpans(0, mStringBuilder.length(), ParcelableSpan.class);
int spanStart[] = new int[spanObjects.length];
int spanEnd[] = new int[spanObjects.length];
int spanFlags[] = new int[spanObjects.length];
for(int i = 0; i < spanObjects.length; ++i)
{
    spanStart[i] = mStringBuilder.getSpanStart(spanObjects[i]);
    spanEnd[i] = mStringBuilder.getSpanEnd(spanObjects[i]);
    spanFlags[i] = mStringBuilder.getSpanFlags(spanObjects[i]);
}

outState.putString("mStringBuilder:string", mStringBuilder.toString());
outState.putParcelableArray("mStringBuilder:spanObjects", spanObjects);
outState.putIntArray("mStringBuilder:spanStart", spanStart);
outState.putIntArray("mStringBuilder:spanEnd", spanEnd);
outState.putIntArray("mStringBuilder:spanFlags", spanFlags);

Then the state can be restored with something like this:

mStringBuilder = new SpannableStringBuilder(savedInstanceState.getString("mStringBuilder:string"));
ParcelableSpan spanObjects[] = (ParcelableSpan[])savedInstanceState.getParcelableArray("mStringBuilder:spanObjects");
int spanStart[] = savedInstanceState.getIntArray("mStringBuilder:spanStart");
int spanEnd[] = savedInstanceState.getIntArray("mStringBuilder:spanEnd");
int spanFlags[] = savedInstanceState.getIntArray("mStringBuilder:spanFlags");
for(int i = 0; i < spanObjects.length; ++i)
    mStringBuilder.setSpan(spanObjects[i], spanStart[i], spanEnd[i], spanFlags[i]);

I've used a SpannableStringBuilder here but it should work with any class implementing Spanned as far as I can tell. It's probably possible to wrap this code into a ParcelableSpanned, but this version seems fine for now.

like image 150
tangobravo Avatar answered Oct 19 '22 01:10

tangobravo


Right now, Html.toHtml() is your only built-in option. Parcelable is used for inter-process communication and is not designed to be durable. If toHtml() does not cover all the particular types of spans that you are using, you will have to cook up your own serialization mechanism.

Since saving the object involves disk I/O, you should be doing that in a background thread anyway, regardless of the speed of toHtml().

like image 20
CommonsWare Avatar answered Oct 19 '22 03:10

CommonsWare


I had a similar problem; I used a SpannableStringBuilder to hold a string and a bunch of spans, and I wanted to be able to save and restore this object. I wrote this code to accomplish this manually using SharedPreferences:

    // Save Log
    SpannableStringBuilder logText = log.getText();
    editor.putString(SAVE_LOG, logText.toString());
    ForegroundColorSpan[] spans = logText
            .getSpans(0, logText.length(), ForegroundColorSpan.class);
    editor.putInt(SAVE_LOG_SPANS, spans.length);
    for (int i = 0; i < spans.length; i++){
        int col = spans[i].getForegroundColor();
        int start = logText.getSpanStart(spans[i]);
        int end = logText.getSpanEnd(spans[i]);
        editor.putInt(SAVE_LOG_SPAN_COLOUR + i, col);
        editor.putInt(SAVE_LOG_SPAN_START + i, start);
        editor.putInt(SAVE_LOG_SPAN_END + i, end);
    }

    // Load Log
    String logText = save.getString(SAVE_LOG, "");
    log.setText(logText);
    int numSpans = save.getInt(SAVE_LOG_SPANS, 0);
    for (int i = 0; i < numSpans; i++){
        int col = save.getInt(SAVE_LOG_SPAN_COLOUR + i, 0);
        int start = save.getInt(SAVE_LOG_SPAN_START + i, 0);
        int end = save.getInt(SAVE_LOG_SPAN_END + i, 0);
        log.getText().setSpan(new ForegroundColorSpan(col), start, end, 
                Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
    }

I my case I knew that all the spans were of type ForegroundColorSpan and with flags SPAN_EXCLUSIVE_EXCLUSIVE, but this code can be easily adapted to accomodate other types.

like image 34
Dan Avatar answered Oct 19 '22 03:10

Dan