Android TextView format few words
Original line:
Lorem ## ipsum ## dolar ## sit ## atem. Lorem ipsum dolar sit ## atem ##.
After formation:
Lorem #ipsum dolar #sit atem. Lorem ipsum dolar sit #atem.
But only the last one has the shape I want. See image below.
CODE
private void format() {
CharSequence text = editContent.getText();
MovementMethod movementMethod = editContent.getMovementMethod();
if ((movementMethod == null) || !(movementMethod instanceof LinkMovementMethod))
{
editContent.setMovementMethod(LinkMovementMethod.getInstance());
}
text = setSpanBetweenTokens(text, "##", new ForegroundColorSpan(0xFF0099FF), new UnderlineSpan(), new ClickableSpan() {
@Override
public void onClick(View widget) {
Toast.makeText(getApplicationContext(), "click", Toast.LENGTH_SHORT).show();
}
});
editContent.setText(text);
}
private static CharSequence setSpanBetweenTokens(CharSequence text, String token, CharacterStyle... characterStyle) {
int tokenLen = token.length();
int start = text.toString().indexOf(token) + 1;
int end = text.toString().indexOf(token, start);
while (start > -1 && end > -1)
{
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(text);
for (CharacterStyle c : characterStyle) {
spannableStringBuilder.setSpan(c, start, end, 0);
}
spannableStringBuilder.delete(end, end + tokenLen);
spannableStringBuilder.delete(start - 1, start);
text = spannableStringBuilder;
start = text.toString().indexOf(token) + 1;
end = text.toString().indexOf(token, start);
}
return text;
}
EDIT
My final decision
private void format() {
CharSequence text = editContent.getText();
MovementMethod movementMethod = editContent.getMovementMethod();
if ((movementMethod == null) || !(movementMethod instanceof LinkMovementMethod))
{
editContent.setMovementMethod(LinkMovementMethod.getInstance());
}
text = setSpanBetweenTokens(text, "##");
editContent.setText(text);
}
private static CharSequence setSpanBetweenTokens(CharSequence text, String token) {
int tokenLen = token.length();
int start = text.toString().indexOf(token) + 1;
int end = text.toString().indexOf(token, start);
while (start > -1 && end > -1)
{
SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(text);
spannableStringBuilder.setSpan(new ForegroundColorSpan(0xFF0099FF), start, end, 0);
spannableStringBuilder.setSpan(new UnderlineSpan(), start, end, 0);
spannableStringBuilder.setSpan(new ClickableSpan() {
@Override
public void onClick(View widget) {
Log.d("DEBUG", "Click");
}
}, start, end, 0);
spannableStringBuilder.delete(end, end + tokenLen);
spannableStringBuilder.delete(start - 1, start);
text = spannableStringBuilder;
start = text.toString().indexOf(token) + 1;
end = text.toString().indexOf(token, start);
}
return text;
}
source to share
Pass a different object for each range:
spannableStringBuilder.setSpan(c, start, end, 0);
You are passing the same object for each range:
new ForegroundColorSpan(0xFF0099FF)
When a span object exists in the spannableStringBuilder, it only changes the bounds, rather than adding a new range.
source to share
I would suggest an easier way. If your formatting needs are basic, a simple + Html.fromHtml () expression should do the trick:
private void format() {
String mText = editContent.getText();
Spanned mSpannedText = Html.fromHtml(mText.replaceAll("##(.*?)##)","<font color=\"0xFF0099\">#$1</font>"),
editContent.setText(mSpannedText);
}
source to share
The final solution loops correctly, but your first token will not be removed correctly as you used
int start = text.toString().indexOf(token) + 1;
which will only work if your token is 1 character long. Since your selected token ## will modify the above code to use the already created tokenLen variable
int start = text.toString().indexOf(token) + tokenLen;
this will ensure correct text editing and remove all traces of your tokens.
source to share