Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use SpannableString with Regex in android?

I am trying to color part of a string using spannableString and regular expressions in Android using this function:

public static String StringReplace(String source) {
        String find = "ABC";
        SpannableString replace = new SpannableString(find);        
        replace.setSpan(new ForegroundColorSpan(Color.RED), 0, 1, Spannable.SPAN_INCLUSIVE_INCLUSIVE);

        String output = source.replace(find, replace);
        return  output;
    }

Because the function replace() returns a string I am not able to get a colored string. My question is: what is the best way to color part of a text using regexp?

like image 871
Kalimah Avatar asked Sep 09 '11 15:09

Kalimah


2 Answers

Updated 10th Sept - changes all occurrences of target string

Something generic like this will work:

public class SpanTest extends Activity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        String dispStr = "This has the string ABCDEF in it \nSo does this :ABCDEF - see!\nAnd again ABCD here";
        TextView tv = (TextView) findViewById(R.id.textView1);
        tv.setText(dispStr);
        changeTextinView(tv, "ABC", Color.RED);
    }

    private void changeTextinView(TextView tv, String target, int colour) {
        String vString = (String) tv.getText();
        int startSpan = 0, endSpan = 0;
        Spannable spanRange = new SpannableString(vString);

        while (true) {
            startSpan = vString.indexOf(target, endSpan);
            ForegroundColorSpan foreColour = new ForegroundColorSpan(colour);
            // Need a NEW span object every loop, else it just moves the span
            if (startSpan < 0)
                break;
            endSpan = startSpan + target.length();
            spanRange.setSpan(foreColour, startSpan, endSpan,
                    Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
        }
        tv.setText(spanRange);
    }

}

.

like image 86
NickT Avatar answered Nov 03 '22 04:11

NickT


Here's a class that will help you:

public class Replacer {
    private final CharSequence mSource;
    private final CharSequence mReplacement;
    private final Matcher mMatcher;
    private int mAppendPosition;
    private final boolean mIsSpannable;

    public static CharSequence replace(CharSequence source, String regex,
            CharSequence replacement) {

        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(source);
        return new Replacer(source, matcher, replacement).doReplace();
    }

    private Replacer(CharSequence source, Matcher matcher,
            CharSequence replacement) {
        mSource = source;
        mReplacement = replacement;
        mMatcher = matcher;
        mAppendPosition = 0;
        mIsSpannable = replacement instanceof Spannable;
    }

    private CharSequence doReplace() {
        SpannableStringBuilder buffer = new SpannableStringBuilder();
        while (mMatcher.find()) {
            appendReplacement(buffer);
        }
        return appendTail(buffer);
    }

    private void appendReplacement(SpannableStringBuilder buffer) {
        buffer.append(mSource.subSequence(mAppendPosition, mMatcher.start()));
        CharSequence replacement = mIsSpannable
                ? copyCharSequenceWithSpans(mReplacement)
                : mReplacement;
        buffer.append(replacement);

        mAppendPosition = mMatcher.end();
    }

    public SpannableStringBuilder appendTail(SpannableStringBuilder buffer) {
        buffer.append(mSource.subSequence(mAppendPosition, mSource.length()));
        return buffer;
    }

    // This is a weird way of copying spans, but I don't know any better way.
    private CharSequence copyCharSequenceWithSpans(CharSequence string) {
        Parcel parcel = Parcel.obtain();
        try {
            TextUtils.writeToParcel(string, parcel, 0);
            parcel.setDataPosition(0);
            return TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(parcel);
        } finally {
            parcel.recycle();
        }
    }
}

And an example of usage:

CharSequence modifiedText = Replacer.replace("ABC aaa AB ABC aa ad ABC", "ABC",
    Html.fromHtml("<font color=\"red\">CBA</font>"));
textView.setText(modifiedText);
like image 6
Michael Avatar answered Nov 03 '22 04:11

Michael