Reputation: 397
I've the following code:
public static SpannableString getSpannable ( String content )
{
SpannableString s = new SpannableString ( content );
Matcher m = Pattern.compile ( "<em>(.*?)</em>" ).matcher ( content );
while ( m.find () )
{ s.setSpan ( new BackgroundColorSpan ( R.color.colorItalic ), m.start ( 1 ), m.end ( 1 ), 0 ); }
return s;
}
It get all <em>(.*?)</em>
and set a BackgroundColorSpan to the group one (.*?)
by your respective position.
It works so well! But the problem is <em>
also is inside string content... after set all spans, how can I remove <em>
from a SpannableString
?
Upvotes: 0
Views: 643
Reputation: 6828
Instead of SpannableString
use SpannableStringBuilder
and use the replace
method.
Try this code,
String content = "Yeah, <em>This</em> is bold.";
SpannableStringBuilder s = new SpannableStringBuilder(content);
String startTag = "<em>";
int startTagLength = startTag.length();
String endTag = "</em>";
int endTagLength = endTag.length();
Matcher m = Pattern.compile(startTag + "(.*?)" + endTag).matcher(content);
while (m.find()) {
Log.e(TAG, "" + m.start(1));
Log.e(TAG, "" + m.end(1));
s.setSpan(new BackgroundColorSpan(R.color.red), m.start(1), m.end(1), 0);
s.replace(m.end(1), m.end(1) + endTagLength, "");
s.replace(m.start(1) - startTagLength, m.start(1), "");
}
TextView out = (TextView) findViewById(R.id.out);
out.setText(s);
EDIT
Caique Monteiro Araujo's has mentioned in the comments that it doesn't work for multiple tags. The reason for this is that the string lengths were getting messed up in the second iteration since I was replacing the string within the while loop itself.
I am happy that Caique Monteiro Araujo has found a workaround. The folloing is mine. I used a TreeMap
to store the (start, end) pairs and replace the tags in another loop. I don't know if this is an overkill.
String content = "<em>This</em> is <em>bold</em>.";
SpannableStringBuilder s = new SpannableStringBuilder(content);
String startTag = "<em>";
int startTagLength = startTag.length();
String endTag = "</em>";
int endTagLength = endTag.length();
Matcher m = Pattern.compile(startTag + "(.*?)" + endTag).matcher(content);
// TreeMap to store the start and end pair
TreeMap<Integer, Integer> pair = new TreeMap<>();
while (m.find()) {
// Store the start and end
pair.put(m.start(1), m.end(1));
s.setSpan(new BackgroundColorSpan(R.color.red), m.start(1), m.end(1), 0);
}
// Use descendingMap to reverse the Map
NavigableMap<Integer, Integer> reversePair = pair.descendingMap();
// Replace the tags starting from the last occurrence to avoid messing the length
for (Integer key : reversePair.keySet()) {
Integer end = reversePair.get(key);
s.replace(end, end + endTagLength, "");
s.replace(key - startTagLength, key, "");
}
TextView out = (TextView) findViewById(R.id.out);
out.setText(s);
Upvotes: 2
Reputation: 397
I've found a solution by using the @K Neeraj Lal help, which I'm so grateful. The point is, his answer didn't work inside the while
loop. So, I had to create a counter to update char position, after a remove the chars. This was necessary because Matcher
is linked with content
variable. So, when it replaces s
variable it changes your length, but the content variable still have the same lenght (where Matcher is linked to). The solution code is:
public static SpannableStringBuilder getSpannable ( String content )
{
SpannableStringBuilder s = new SpannableStringBuilder ( content );
String startAt = "<em>";
String endAt = "</em>";
Matcher m = Pattern.compile ( startAt + "(.*?)" + endAt ).matcher ( content );
int counter = 0, length = startAt.length() + endAt.length();
int startA, startB, endA, endB;
while ( m.find () )
{
startA = m.start()-counter;
endA = m.end(1)-counter;
startB = m.start(1)-counter;
endB = m.end()-counter;
s.setSpan ( new BackgroundColorSpan ( R.color.colorItalic ), startB, endA, 0 );
s.replace ( endA, endB, "" );
s.replace ( startA, startB, "" );
counter += length;
}
return s;
}
Anyway, I don't know if it is the best approach.
Upvotes: 0