/** * Remove and/or add {@link Spanned.SPAN_EXCLUSIVE_EXCLUSIVE} spans, since they should only exist * as long as the text they cover is the same. All other spans will remain the same, since they * will adapt to the new text, hence why {@link SpannableStringBuilder#replace} never removes * them. */ private void manageSpans(SpannableStringBuilder spannableStringBuilder) { Object[] spans = getText().getSpans(0, length(), Object.class); for (int spanIdx = 0; spanIdx < spans.length; spanIdx++) { // Remove all styling spans we might have previously set if (ForegroundColorSpan.class.isInstance(spans[spanIdx]) || BackgroundColorSpan.class.isInstance(spans[spanIdx]) || AbsoluteSizeSpan.class.isInstance(spans[spanIdx]) || CustomStyleSpan.class.isInstance(spans[spanIdx]) || ReactTagSpan.class.isInstance(spans[spanIdx])) { getText().removeSpan(spans[spanIdx]); } if ((getText().getSpanFlags(spans[spanIdx]) & Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) != Spanned.SPAN_EXCLUSIVE_EXCLUSIVE) { continue; } Object span = spans[spanIdx]; final int spanStart = getText().getSpanStart(spans[spanIdx]); final int spanEnd = getText().getSpanEnd(spans[spanIdx]); final int spanFlags = getText().getSpanFlags(spans[spanIdx]); // Make sure the span is removed from existing text, otherwise the spans we set will be // ignored or it will cover text that has changed. getText().removeSpan(spans[spanIdx]); if (sameTextForSpan(getText(), spannableStringBuilder, spanStart, spanEnd)) { spannableStringBuilder.setSpan(span, spanStart, spanEnd, spanFlags); } } }