Original String:
Lorem ##ipsum## dolar ##sit## atem. Lorem ipsum dolar sit ##atem##.
After formating:
Lorem #ipsum dolar #sit atem. Lorem ipsum dolar sit #atem.
But only the last one has the Formating 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 Solution
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;
}
Pass different object for each span:
spannableStringBuilder.setSpan(c, start, end, 0);
You're passing the same object for each span:
new ForegroundColorSpan(0xFF0099FF)
When span object exists in spannableStringBuilder then it changes bounds only, not a new span is added.
I would suggest a simpler way. If your formatting needs are basic, a simple regex + Html.fromHtml() 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);
}
The final solution correctly loops however your first token will not be correctly deleted as you have used
int start = text.toString().indexOf(token) + 1;
which would only work if your token was 1 character in length. Since your chosen token is ## change the above code to utilise the already created variable tokenLen
int start = text.toString().indexOf(token) + tokenLen;
this will ensure your text is correctly edited and all trace of your tokens are removed.
Related
I have an edit text while typing inside edit text if the word starts with # that particular words color should change ,
i have implemented textwatcher and found that if text starts with # but do not know how to update the color dynamically ,
Have tried SpannableStringBuilder ssb = new SpannableStringBuilder(yourText) But its static , could anyone help me for dynamic implementation
Here is my code
myEditTxt.addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence text, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence text, int start, int before, int count) {
if (text.charAt(start) == '#') {
//here i needs to update the typing text color
}
}
#Override
public void afterTextChanged(Editable s) {
}
});
As implemented in this link How to change color of words with hashtags #Muhammed GÜNEŞ suggested
you can modify it according to follow
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
textView.removeTextChangedListener(this);
setTags(textView,s.toString());
textView.setSelection(textView.getText().toString().length());
textView.addTextChangedListener(this);
}
private void setTags(TextView pTextView, String pTagString) {
SpannableString string = new SpannableString(pTagString);
int start = -1;
for (int i = 0; i < pTagString.length(); i++) {
if (pTagString.charAt(i) == '#') {
start = i;
} else if (pTagString.charAt(i) == ' ' || (i == pTagString.length() - 1 && start != -1)) {
if (start != -1) {
if (i == pTagString.length() - 1) {
i++; // case for if hash is last word and there is no
// space after word
}
final String tag = pTagString.substring(start, i);
string.setSpan(new ClickableSpan() {
#Override
public void onClick(View widget) {
Toast.makeText(MainActivity.this,"Click",Toast.LENGTH_LONG).show();
}
#Override
public void updateDrawState(TextPaint ds) {
// link color
ds.setColor(Color.parseColor("#33b5e5"));
ds.setUnderlineText(false);
}
}, start, i, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
start = -1;
}
}
}
pTextView.setMovementMethod(LinkMovementMethod.getInstance());
pTextView.setText(string);
}
it will add color and click event too also u can modify it according o your further requirement
Maybe something like this
#Override
public void onTextChanged(CharSequence text, int start, int before, int count) {
if (text.charAt(start) == '#') {
SpannableString spannableString = new SpannableString(editText.getText().toString());
ForegroundColorSpan foregroundSpan = new ForegroundColorSpan(ContextCompat.getColor(getContext(), R.color.yourColor));
spannableString.setSpan(foregroundSpan, start,
spannableString.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
editText.setText(spannableString);
}
}
If you only want to change "#" char. You can try it:
int index = myEditTxt.getText().toString().indexOf("#");
if (index != -1) {
SpannableString spannable = new
SpannableString(myEditTxt.getText().toString());
ForegroundColorSpan fcs = new ForegroundColorSpan(color);
// Set the text color
spannable.setSpan(fcs, index, index + 1,
Spannable.SPAN_INCLUSIVE_INCLUSIVE);
mEditText.setText(spannable);
}
If you only want to change after and before "#". You can try it in your listener:
if (text.charAt(start) == '#') {
SpannableString spannable = new
SpannableString(myEditTxt.getText().toString());
ForegroundColorSpan fcs = new ForegroundColorSpan(color);
// Set the text color
if (start > 0)
spannable.setSpan(fcs, 0, start -1,
Spannable.SPAN_INCLUSIVE_INCLUSIVE);
if (start < myEditTxt.getText().toString().length())
spannable.setSpan(fcs, start + 1, myEditTxt.getText().toString().length(),
Spannable.SPAN_INCLUSIVE_INCLUSIVE);
mEditText.setText(spannable);
}
Finally found an answer and works as expected .
private int intCount = 0, initialStringLength = 0;
private String strText = "";
on text changed
#Override
public void onTextChanged(CharSequence text, int start, int before, int count) {
String strET = editText.getText().toString();
String[] str = strET.split(" ");
int cnt = 0;
if (text.length() != initialStringLength && text.length() != 0) {
if (!strET.substring(strET.length() - 1).equals(" ")) {
initialStringLength = text.length();
cnt = intCount;
for (int i = 0; i < str.length; i++)
if (str[i].charAt(0) == '#')
strText = strText + " " + "<font color='#EE0000'>" + str[i] + "</font>";
else
strText = strText + " " + str[i];
}
if (intCount == cnt) {
intCount = str.length;
editText.setText(Html.fromHtml(strText));
editText.setSelection(textShareMemories.getText().toString().length());
}
} else {
strText = "";
}
}
Is there a way to prevent a user from deleting or modifying a spannable in an EditText? More specifically, I have an ImageSpan as the first character of the EditText. I want to ensure the user cannot delete that ImageSpan.
I realize I can use TextWatcher and replace the ImageSpan if the user deletes it. That's rather ugly and I'm hoping there's a way to prevent the deletion in the first place.
Here's a snip of code where I set the text value:
Bitmap bitmap = <bitmap from elsewhere>;
String text = <text to display after ImageSpan, from elsewhere>;
SpannableString ss = new SpannableString (" " + text);
ImageSpan image = new ImageSpan (getContext(), bitmap, ImageSpan.ALIGN_BOTTOM);
ss.setSpan (image, 0, 1, 0);
setText (ss);
OK, the solution is quite complicated but it's working. We need a custom InputFilter and a SpanWatcher. Let's start.
The first step is quite simple. We set a non-editable prefix with an image span and set a cursor after this prefix.
final String prefix = "?";
final ImageSpan image = new ImageSpan(this, R.drawable.image);
edit.setText(prefix);
edit.getText().setSpan(image, 0, prefix.length(), Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
edit.setSelection(prefix.length());
Then we set an InputFilter that will prevent the prefix with the span from editing. It can be done by moving a range being edited so it starts after the prefix.
edit.setFilters(new InputFilter[] {
new InputFilter() {
#Override
public CharSequence filter(final CharSequence source, final int start,
final int end, final Spanned dest, final int dstart, final int dend) {
final int newStart = Math.max(prefix.length(), dstart);
final int newEnd = Math.max(prefix.length(), dend);
if (newStart != dstart || newEnd != dend) {
final SpannableStringBuilder builder = new SpannableStringBuilder(dest);
builder.replace(newStart, newEnd, source);
if (source instanceof Spanned) {
TextUtils.copySpansFrom(
(Spanned) source, 0, source.length(), null, builder, newStart);
}
Selection.setSelection(builder, newStart + source.length());
return builder;
} else {
return null;
}
}
}
});
Then we create a SpanWatcher that will detect selection changes and move the selection out of the prefix range.
final SpanWatcher watcher = new SpanWatcher() {
#Override
public void onSpanAdded(final Spannable text, final Object what,
final int start, final int end) {
// Nothing here.
}
#Override
public void onSpanRemoved(final Spannable text, final Object what,
final int start, final int end) {
// Nothing here.
}
#Override
public void onSpanChanged(final Spannable text, final Object what,
final int ostart, final int oend, final int nstart, final int nend) {
if (what == Selection.SELECTION_START) {
if (nstart < prefix.length()) {
final int end = Math.max(prefix.length(), Selection.getSelectionEnd(text));
Selection.setSelection(text, prefix.length(), end);
}
} else if (what == Selection.SELECTION_END) {
final int start = Math.max(prefix.length(), Selection.getSelectionEnd(text));
final int end = Math.max(start, nstart);
if (end != nstart) {
Selection.setSelection(text, start, end);
}
}
}
};
And finally we just add the SpanWatcher to the text.
edit.getText().setSpan(watcher, 0, 0, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
And that's all. So comparing this solution to just adding a TextWatcher I would prefer the latter approach.
I am trying to implement code highlighting using spanned text and html.fromhtml() function in an edittext than implements text watcher.
The problem occurs when i try to manipulate cursor for custom brackets, the app crashes due to some spannable string setspan error.
How do i use spannable code highlighting and set cursor position adjusting according to the spanned text.
Edit:
The function:
Spanned matchtext(String s)
{
//Pattern p =Pattern.compile(check[0]);
String a=s;
for(int i=0;i<Constants.keyWords.length;i++) {
a = a.replaceAll(Constants.keyWords[i], "<font color=\"#c5c5c5\">" + Constants.keyWords[i] + "</font>");
//a = s.replaceAll(";", "<font color=\"#c5c5c5\">" + ";" + "</font>");
}
Spanned ab = Html.fromHtml(a);
return ab;
}
And the function call:
mCodeEditText.removeTextChangedListener(tt);
bs = matchtext(s.toString());
mCodeEditText.setText(bs);
mCodeEditText.addTextChangedListener(tt);
Edit 2:
This is my new implementation, I just can't get the highlighting to work.
Spannable matchtext(String s, int pos) {
Spannable abc = new SpannableString(s);
for (int i = 0; i < Constants.keyWords.length; i++) {
if (pos - Constants.keyWords[i].length() >= 0) {
int j = s.indexOf(Constants.keyWords[i]);
if (j != -1) {
if ((s.subSequence(j, pos)).equals(Constants.keyWords[i]))
abc.setSpan(new ForegroundColorSpan(Color.BLUE), j, pos, Spannable.SPAN_INCLUSIVE_INCLUSIVE);
}
}
}
return abc;
}
i am making a chatting application in which i am using emoticons functionality.My Emoticons functionality is working properly for single image ,but when i am taking multiple emotive images it is not converting in to particular image..,at a time only single image is converting, My problem is
i am unable to separate the spanned object in edit text field..,for single value it is working but for multiple value its is not working..
Example.i am taking 4 different images in edit text field, like this here
now i want to seprate its spanned object.,how can i do this
here is code
public void keyClickedIndex( final String index)
{
ImageGetter imageGetter = new ImageGetter()
{
public Drawable getDrawable(String source)
{
StringTokenizer st = new StringTokenizer(index, ".");
Drawable d = new BitmapDrawable(getResources(),emoticons[Integer.parseInt(st.nextToken()) - 1]);
d.setBounds(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
return d;
}
};
Spanned cs = Html.fromHtml("<img src ='"+ index +"'/>", imageGetter, null);
int cursorPosition = mSendText.getSelectionStart();
mSendText.getText().insert(cursorPosition, cs);
please help me..,Thanks in Advance.
you can use emoticon handler method
private static class EmoticonHandler implements TextWatcher {
private final EditText mEditor;
private final ArrayList<ImageSpan> mEmoticonsToRemove = new ArrayList<ImageSpan>();
//public String txt;
XMPPClient act;
public EmoticonHandler(EditText editor,XMPPClient act) {
// Attach the handler to listen for text changes.
mEditor = editor;
mEditor.addTextChangedListener(this);
this.act = act;
}
public void insert(String emoticon, int resource)
{
// Create the ImageSpan
Drawable drawable = mEditor.getResources().getDrawable(resource);
drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
ImageSpan span = new ImageSpan(drawable,emoticon,ImageSpan.ALIGN_BASELINE);
// Get the selected text.
int start = mEditor.getSelectionStart();
int end = mEditor.getSelectionEnd();
Editable message = mEditor.getEditableText();
// Insert the emoticon.
message.replace(start, end, emoticon);
message.setSpan(span, start, start + emoticon.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
#Override
public void beforeTextChanged(CharSequence text, int start, int count, int after) {
// Check if some text will be removed.
if (count > 0) {
int end = start + count;
Editable message = mEditor.getEditableText();
ImageSpan[] list = message.getSpans(start, end, ImageSpan.class);
boolean check = false;
for (ImageSpan span : list)
{
// Get only the emoticons that are inside of the changed
// region.
check = true;
int spanStart = message.getSpanStart(span);
int spanEnd = message.getSpanEnd(span);
//txt = text.toString();
act.emorTxt = text.toString();
if ((spanStart < end) && (spanEnd > start)) {
// Add to remove list
mEmoticonsToRemove.add(span);
}
}
if(!check)
{
act.emorTxt = text.toString();
}
}
}
#Override
public void afterTextChanged(Editable text) {
Editable message = mEditor.getEditableText();
// Commit the emoticons to be removed.
for (ImageSpan span : mEmoticonsToRemove)
{
int start = message.getSpanStart(span);
int end = message.getSpanEnd(span);
// Remove the span
message.removeSpan(span);
// Remove the remaining emoticon text.
if (start != end) {
message.delete(start, end);
}
}
mEmoticonsToRemove.clear();
}
#Override
public void onTextChanged(CharSequence text, int start, int before, int count) {
}
}
it will work perfectly....:)
I want to add underline and colour to textview, but it is a simple text, not link, not phone number, just simple "Hello world", that I want to have with underline and that blue link-like colour.
It failed to do so:
view.setMovementMethod(LinkMovementMethod.getInstance());
Linkify.addLinks(view, Linkify.ALL);
Thank you! I just underlined the text as Const suggested and changed color of textview. But I guess xoxol_89's answer is correct in my case and should be accepted.
Do you try to use Spannable
For example
/**
* Method allocates filtering substring in all contacts yellow color,
* that satisfy the user's search
* #param inputText - DisplayName
* filtText - filtering Text
* #return String with allocating substring (Spannable)
*/
public static Spannable changeBackgroungFiltText(CharSequence inputText, String filtText, int color) {
Spannable str = null;
if(inputText != null)
{
String inputStr = inputText.toString();
String inputLowerCaseStr = inputStr.toLowerCase();
String filtLowerCaseStr = filtText.toLowerCase();
// Spannable str = new SpannableStringBuilder(inputStr);
str = new SpannableStringBuilder(inputStr);
if (filtText.length() != 0)
{
int indexStart = 0;
while (true)
{
int indexCur = inputLowerCaseStr.indexOf(filtLowerCaseStr, indexStart);
if (indexCur != -1) {
int start = indexCur;
int end = indexCur + filtText.length();
int flag = Spannable.SPAN_EXCLUSIVE_EXCLUSIVE;
str.setSpan(new ForegroundColorSpan(color),start, end, flag);
//str.setSpan(new BackgroundColorSpan(highlightColor), start, end, flag);
indexStart = indexCur + 1;
} else {
return str;
}
}
} else {
return str;
}
}
return str;
}
You can do that in 4 ways:
1.Automatically linkifies using android:autoLink=”all”
2.Link text by setMovementMethod
3.Link as html code using Html.fromHtml()
4.Link string by SpannableString
and you can find the examples here
You can use SpannableString like this:
final SpannableString text = new SpannableString("Hello World!");
final int startAt = 0;
final int endAt = text.length();
final int sampleColor = Color.parseColor("#3333ff");
text.setSpan(new UnderlineSpan(), startAt, endAt, 0);
text.setSpan(new ForegroundColorSpan(sampleColor), startAt, endAt, 0);
textView.setText(text);