javaandroidregex

Android: colorize matches within string


I want to color all words starting with #, + or @. I found solutions to match words and to color substrings of a SpannableString, but it does not work right. Only the last match gets coloured the other matches stay uncoloured, but I am not able to find the error.

public static SpannableStringBuilder colorTags(Context cxt, String text){
    final Pattern hashtag = Pattern.compile("#\\w+");
    final Pattern at = Pattern.compile("(\\s|\\A)@(\\w+)");
    final Pattern name = Pattern.compile("(\\s|\\A)\\+(\\w+)");

    final SpannableStringBuilder spannable = new SpannableStringBuilder(text);

    final ForegroundColorSpan at_color = new ForegroundColorSpan(cxt.getResources().getColor(R.color.orange));
    final ForegroundColorSpan hash_color = new ForegroundColorSpan(cxt.getResources().getColor(R.color.light_blue));
    final ForegroundColorSpan name_color = new ForegroundColorSpan(cxt.getResources().getColor(R.color.green));

    final Matcher matcher = hashtag.matcher(text);
    final Matcher at_matcher = at.matcher(text);
    final Matcher name_matcher = name.matcher(text);


    while (matcher.find()) {
        spannable.setSpan(
                hash_color, matcher.start(), matcher.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        );
    }

    while (at_matcher.find()) {
        spannable.setSpan(
                at_color, at_matcher.start(), at_matcher.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        );
    }

    while (name_matcher.find()) {
        spannable.setSpan(
                name_color, name_matcher.start(), name_matcher.end(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
        );
    }
    return spannable;
}

Solution

  • You need a new object for each span, or else it will be moved and end up on the last span.

    while (matcher.find()) {
        ForegroundColorSpan hash_color =
            ForegroundColorSpan(cxt.getResources().getColor(R.color.light_blue));
        spannable.setSpan( hash_color, matcher.start(), matcher.end(),
                           Spanned.SPAN_EXCLUSIVE_EXCLUSIVE );
    }
    

    Same for the other loops.