Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Clone SpannableStringBuilder

I am working on an Activity in which I parse a text with markup characters. What I'm doing is converting them to several types of ClickableSpans.

The problem is that I need to implement a function (lets call it function B) that implies having overlapping ClickableSpans and that causes several issues.

So what I'm doing now is creating a new SSB while detecting the overlapping spans, and removing the ones I don't need for this particular function. Working fine.

BUT, I need to be able to go back to the previous SSB and that doesn't seem to work.

STEP BY STEP:

// I CREATE THE SSBs
...
static SpannableStringBuilder ssb;
static SpannableStringBuilder ssbCopy;

// I IMPLEMENT MY CUSTOM FUNCTION THAT PARSES THE TEXT AND SETS THE SBB AS A TEXTVIEW CONTENT
...
textView.setMovementMethod(new LinkTouchMovementMethod());
ssb = addClickablePart(chapterTextStr, markupCharactersArray);
textView.setText(ssb);

// WHEN A BUTTON IS CLICKED I IMPLEMENT MY FUNCTION B. WHERE I CREATE A COPY OF MY ORIGINAL SSB AND STORE IT IN ssbCopy, AND SET IT AS THE TEXTVIEW CONTENT
...
ssbCopy = SpannableStringBuilder.valueOf(ssb);

// I REMOVE THE OVERLAPPING SPANS
...
overlapSpans = ssbCopy.getSpans(index, index+word.length(), TouchableSpan.class);

for (int c=0;c<overlapSpans.length;c++) {                       
    ssbCopy.removeSpan(overlapSpans[c]);
}                       

// I SET THE NEW CLICKABLE SPANS
...
ssbCopy.setSpan(touchableSpan, index, index + word.length(), 0);

// AND SET THE NEW SSB CONTENT TO THE TEXTVIEW
textView.setText(ssbCopy);

// EVERYTHING WORKS FINE UP TO HERE
// BUT WHEN I TRY TO SET BACK THE ORIGINAL SSB BACK AS THE CONTENT OF MY TEXTVIEW WHEN THE USER CLICKS A BUTTON
...
textView.setText(ssb);

// THE ORIGINAL SSB IS EXACTLY LIKE THE COPY (ssbCopy) AND CONTAINS THE SAME CLICKABLE SPANS I ADDED. NOT ONLY THE ORIGINAL ONES

I guess it may sound somewhat confusing and I'm not sure if I explained properly, but I can't get around this.

EDIT:

As per kcoppock answer, I learn it isn't possible to clone an ssb and valueOf(ssb) is just a copy of the object. So I ended up cloning my "ssb" manually by looping through all elements and applying them to the new ssb. Like this:

TouchableSpan[] spans = ssb.getSpans(0, ssb.length(), TouchableSpan.class);

ssbCopy = new SpannableStringBuilder(chapterTextStr+"dsadsa");

for (int c=0;c<spans.length;c++) {
    TouchableSpan obj = spans[c];

    ssbCopy.setSpan(obj, ssb.getSpanStart(obj), ssb.getSpanEnd(obj), 0);
}

By the way, TouchableSpan is a custom class I created that extends ClickableSpan

like image 927
Cruclax Avatar asked Jul 17 '14 22:07

Cruclax


2 Answers

The problem here is your use of valueOf(). It doesn't do what you think it does. All it does is return the object passed in, if it is a SpannableStringBuilder; otherwise, it wraps the given CharSequence in a SpannableStringBuilder. From the source:

public static SpannableStringBuilder valueOf(CharSequence source) {
    if (source instanceof SpannableStringBuilder) {
        return (SpannableStringBuilder) source;
    } else {
        return new SpannableStringBuilder(source);
    }
}

So essentially:

ssb == SpannableStringBuilder.valueOf(ssb);

They are one and the same Object. SpannableStringBuilder does not implement Cloneable, so there's no easy way to make a copy, other than to just generate two copies, for example:

ssb = addClickablePart(chapterTextStr, markupCharactersArray);
ssbCopy = addClickablePart(chapterTextStr, markupCharactersArray);
like image 115
Kevin Coppock Avatar answered Sep 22 '22 14:09

Kevin Coppock


The method subsequence in SpannableStringBuilder copies the spans. You could make a copy of you ssb by calling ssb.subsequence(0, ssb.length)

SpannableStringBuilder ssbCopy = (SpannableStringBuilder)ssb.subSequence(0, ssb.length());
like image 24
Becca Avatar answered Sep 23 '22 14:09

Becca