I am making a basic text editor app for Android and currently working on formatting the text.
I have an EditText named text_area
where the user types his text and a ToggleButton called bold
that sets the text to bold. Initially, using the EditText.setTypeface
method, all of the text in text_area
would change to bold when the button is on. Using the answer provided in this question, I was able to change only the selected text to bold.
What I really want to do though is that when the button is pressed, all the previously typed text (normal and/or bold) remain unchanged, and whatever the user types next is typed in bold.
Here's my code (Could someone also tell me what the code under the else statement does):
bold.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
if(bold.isChecked()==true) {
Spannable str = textarea.getText();
if(textarea.getSelectionEnd() > textarea.getSelectionStart())
str.setSpan(new StyleSpan(android.graphics.Typeface.BOLD),
textarea.getSelectionStart(), textarea.getSelectionEnd(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
else
str.setSpan(new StyleSpan(android.graphics.Typeface.BOLD),
textarea.getSelectionEnd(),
textarea.getSelectionStart(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
});
I was stuck on the same issue, and after hours of trying, this is what I came up with.
First, whenever you check the bold/italics whatever styled checkbox you want to apply, you want to get the current cursor position.
//if bold is checked
YourEditText.getSelectionStart();
YourEditText.getSelectionEnd();
both gives you the current cursor position of the EditText if no text is highlighted.
Store this value into a variable.
int lastCursorPosition = YourEditText.getSelectionStart();
Then, I overrode the onTextChanged function of the EditText. Since we only want to set span from the last cursor position to the end of wherever change is made, we set the span from lastCursorPosition to the end of the text.
int endOfString = YourEditText.getText().toString().length();
StyleSpan ss = new StyleSpan(Typeface.BOLD);
str.setSpan(ss, lastCursorPosition, endOfString, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
While doing this, I ran into another problem. Whenever I applied another span to another part of the text, previous styles disappeared. I fixed this by creating new StyleSpan for each time a new style was applied. Minimal code to understand:
public static final int TYPEFACE_NORMAL = 0;
public static final int TYPEFACE_BOLD = 1;
public static final int TYPEFACE_ITALICS = 2;
public static final int TYPEFACE_BOLD_ITALICS = 3;
private int currentTypeface;
private int lastCursorPosition;
...
@Override
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
Spannable str = this.getText();
StyleSpan ss;
int endOfString = text.toString().length();
//current typeface is determined by bold, italics, checkboxes, etc
switch(currentTypeface) {
case TYPEFACE_NORMAL:
ss = new StyleSpan(Typeface.NORMAL);
break;
case TYPEFACE_BOLD:
ss = new StyleSpan(Typeface.BOLD);
break;
case TYPEFACE_ITALICS:
ss = new StyleSpan(Typeface.ITALIC);
break;
case TYPEFACE_BOLD_ITALICS:
ss = new StyleSpan(Typeface.BOLD_ITALIC);
break;
default:
ss = new StyleSpan(Typeface.NORMAL);
}
str.setSpan(ss, lastCursorPosition, endOfString, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
Full TextArea class I've written:
public class TextArea extends EditText {
public static final int TYPEFACE_NORMAL = 0;
public static final int TYPEFACE_BOLD = 1;
public static final int TYPEFACE_ITALICS = 2;
public static final int TYPEFACE_BOLD_ITALICS = 3;
private int currentTypeface;
private int lastCursorPosition;
private int tId;
public TextArea(Context context) {
super(context);
lastCursorPosition = this.getSelectionStart();
}
public TextArea(Context context, AttributeSet attrs) {
super(context, attrs);
}
public int gettId() {
return tId;
}
public void settId(int tId) {
this.tId = tId;
}
public void changeTypeface(int tfId) {
currentTypeface = tfId;
lastCursorPosition = this.getSelectionStart();
}
@Override
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
Spannable str = this.getText();
StyleSpan ss;
int endLength = text.toString().length();
switch(currentTypeface) {
case TYPEFACE_NORMAL:
ss = new StyleSpan(Typeface.NORMAL);
break;
case TYPEFACE_BOLD:
ss = new StyleSpan(Typeface.BOLD);
break;
case TYPEFACE_ITALICS:
ss = new StyleSpan(Typeface.ITALIC);
break;
case TYPEFACE_BOLD_ITALICS:
ss = new StyleSpan(Typeface.BOLD_ITALIC);
break;
default:
ss = new StyleSpan(Typeface.NORMAL);
}
str.setSpan(ss, lastCursorPosition, endLength, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
From MainActivity.java
TextArea t = new TextArea(context);
int typefaceStyle = TextArea.TYPEFACE_NORMAL;
CheckBox boldCheckbox = (CheckBox) findViewById(R.id.post_bold_checkbox);
boldCheckbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
boldChecked = isChecked;
if(italicsChecked && boldChecked) {
typefaceStyle = TextArea.TYPEFACE_BOLD_ITALICS;
} else if (boldChecked){
typefaceStyle = TextArea.TYPEFACE_BOLD;
} else if (italicsChecked) {
typefaceStyle = TextArea.TYPEFACE_ITALICS;
} else {
typefaceStyle = TextArea.TYPEFACE_NORMAL;
}
t.changeTypeface(typefaceStyle);
}
});
My very first reply on StackOverflow! Hope this helped :-)