|
|
package net.micode.notes.ui;
|
|
|
|
|
|
import android.content.Context;
|
|
|
import android.os.Build;
|
|
|
import android.text.Editable;
|
|
|
import android.text.TextWatcher;
|
|
|
import android.text.style.StrikethroughSpan;
|
|
|
import android.text.style.StyleSpan;
|
|
|
import android.text.style.UnderlineSpan;
|
|
|
import android.util.AttributeSet;
|
|
|
import android.widget.EditText;
|
|
|
import android.graphics.Typeface;
|
|
|
import android.text.Spannable;
|
|
|
|
|
|
public class RichEditor extends EditText {
|
|
|
|
|
|
public interface OnTextChangeListener {
|
|
|
void onTextChange(String text);
|
|
|
}
|
|
|
|
|
|
private OnTextChangeListener onTextChangeListener;
|
|
|
|
|
|
public RichEditor(Context context) {
|
|
|
super(context);
|
|
|
init();
|
|
|
}
|
|
|
|
|
|
public RichEditor(Context context, AttributeSet attrs) {
|
|
|
super(context, attrs);
|
|
|
init();
|
|
|
}
|
|
|
|
|
|
public RichEditor(Context context, AttributeSet attrs, int defStyleAttr) {
|
|
|
super(context, attrs, defStyleAttr);
|
|
|
init();
|
|
|
}
|
|
|
|
|
|
private void init() {
|
|
|
// 初始化逻辑
|
|
|
}
|
|
|
|
|
|
public void setOnTextChangeListener(OnTextChangeListener listener) {
|
|
|
this.onTextChangeListener = listener;
|
|
|
|
|
|
// 监听文本变化
|
|
|
addTextChangedListener(new TextWatcher() {
|
|
|
@Override
|
|
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
|
|
|
|
|
@Override
|
|
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
|
|
if (onTextChangeListener != null) {
|
|
|
onTextChangeListener.onTextChange(s.toString());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public void afterTextChanged(Editable s) {}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
// 重写获取文本的方法
|
|
|
@Override
|
|
|
public Editable getText() {
|
|
|
return super.getText();
|
|
|
}
|
|
|
|
|
|
// 重写设置文本的方法,包括设置文本颜色、文本样式等
|
|
|
@Override
|
|
|
public void setText(CharSequence text, BufferType type) {
|
|
|
super.setText(text, type);
|
|
|
}
|
|
|
|
|
|
// 重写设置光标位置的方法
|
|
|
@Override
|
|
|
public void setSelection(int index) {
|
|
|
super.setSelection(index);
|
|
|
}
|
|
|
|
|
|
// 重写设置选中文本的方法
|
|
|
@Override
|
|
|
public void setSelection(int start, int stop) {
|
|
|
super.setSelection(start, stop);
|
|
|
}
|
|
|
|
|
|
// 重写获取选中文本起始位置的方法
|
|
|
@Override
|
|
|
public int getSelectionStart() {
|
|
|
return super.getSelectionStart();
|
|
|
}
|
|
|
|
|
|
// 重写获取选中文本结束位置的方法
|
|
|
@Override
|
|
|
public int getSelectionEnd() {
|
|
|
return super.getSelectionEnd();
|
|
|
}
|
|
|
|
|
|
// 重写设置文本外观的方法,文本外观包括字体、颜色等
|
|
|
@Override
|
|
|
public void setTextAppearance(Context context, int resid) {
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
|
super.setTextAppearance(resid);
|
|
|
} else {
|
|
|
// 对旧版本的兼容处理
|
|
|
setTextAppearance(context, resid);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 设置编辑器高度
|
|
|
public void setEditorHeight(int pixels) {
|
|
|
this.setHeight(pixels);
|
|
|
}
|
|
|
|
|
|
|
|
|
//设置编辑器字体大小
|
|
|
public void setEditorFontSize(int size) {
|
|
|
this.setTextSize(size);
|
|
|
}
|
|
|
|
|
|
//设置编辑器字体颜色
|
|
|
public void setEditorFontColor(int color) {
|
|
|
this.setTextColor(color);
|
|
|
}
|
|
|
|
|
|
//设置占位符文本
|
|
|
public void setPlaceholder(String placeholder) {
|
|
|
this.setHint(placeholder);
|
|
|
}
|
|
|
|
|
|
// 设置编辑器启用或禁用输入
|
|
|
public void setInputEnabled(boolean enabled) {
|
|
|
this.setEnabled(enabled);
|
|
|
}
|
|
|
|
|
|
//切换粗体样式
|
|
|
public void toggleBold() {
|
|
|
toggleStyleSpan(Typeface.BOLD);
|
|
|
}
|
|
|
|
|
|
//切换斜体样式
|
|
|
public void toggleItalic() {
|
|
|
toggleStyleSpan(Typeface.ITALIC);
|
|
|
}
|
|
|
|
|
|
//切换删除线样式
|
|
|
public void toggleStrikeThrough() {
|
|
|
toggleSpan(StrikethroughSpan.class, new StrikethroughSpan());
|
|
|
}
|
|
|
|
|
|
//切换下划线样式
|
|
|
public void toggleUnderline() {
|
|
|
toggleSpan(UnderlineSpan.class, new UnderlineSpan());
|
|
|
}
|
|
|
|
|
|
// 粗体、斜体样式切换方法
|
|
|
private void toggleStyleSpan(int style) {
|
|
|
int start = getSelectionStart();
|
|
|
int end = getSelectionEnd();
|
|
|
if (start < 0 || end < 0 || start >= end) {
|
|
|
return; // 没有选中文本时不处理
|
|
|
}
|
|
|
|
|
|
Spannable str = getText();
|
|
|
StyleSpan[] spans = str.getSpans(start, end, StyleSpan.class);
|
|
|
|
|
|
boolean hasStyle = false;
|
|
|
// 检查是否已存在相同样式
|
|
|
for (StyleSpan span : spans) {
|
|
|
if (span.getStyle() == style) {
|
|
|
str.removeSpan(span);
|
|
|
hasStyle = true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 如果不存在对应样式,则添加
|
|
|
if (!hasStyle) {
|
|
|
str.setSpan(new StyleSpan(style), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
//通用的Span切换方法,下划线和删除线
|
|
|
private <T> void toggleSpan(Class<T> spanType, T newSpan) {
|
|
|
int start = getSelectionStart();
|
|
|
int end = getSelectionEnd();
|
|
|
if (start < 0 || end < 0 || start >= end) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
Spannable str = getText();
|
|
|
@SuppressWarnings("unchecked")
|
|
|
T[] spans = (T[]) str.getSpans(start, end, spanType);
|
|
|
|
|
|
if (spans.length > 0) {
|
|
|
// 如果已存在样式,则移除
|
|
|
for (T span : spans) {
|
|
|
str.removeSpan(span);
|
|
|
}
|
|
|
} else {
|
|
|
// 如果不存在样式,则添加
|
|
|
str.setSpan(newSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
// 应用粗体样式
|
|
|
public void applyBold() {
|
|
|
int start = getSelectionStart();
|
|
|
int end = getSelectionEnd();
|
|
|
|
|
|
if (start >= 0 && end >= 0 && start != end) {
|
|
|
Spannable str = getText();
|
|
|
StyleSpan[] spans = str.getSpans(start, end, StyleSpan.class);
|
|
|
|
|
|
for (StyleSpan span : spans) {
|
|
|
if (span.getStyle() == Typeface.BOLD) {
|
|
|
str.removeSpan(span);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
str.setSpan(new StyleSpan(Typeface.BOLD), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 应用斜体样式
|
|
|
public void applyItalic() {
|
|
|
int start = getSelectionStart();
|
|
|
int end = getSelectionEnd();
|
|
|
|
|
|
if (start >= 0 && end >= 0 && start != end) {
|
|
|
Spannable str = getText();
|
|
|
StyleSpan[] spans = str.getSpans(start, end, StyleSpan.class);
|
|
|
|
|
|
for (StyleSpan span : spans) {
|
|
|
if (span.getStyle() == Typeface.ITALIC) {
|
|
|
str.removeSpan(span);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
str.setSpan(new StyleSpan(Typeface.ITALIC), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 应用删除线样式
|
|
|
public void applyStrikeThrough() {
|
|
|
int start = getSelectionStart();
|
|
|
int end = getSelectionEnd();
|
|
|
|
|
|
if (start >= 0 && end >= 0 && start != end) {
|
|
|
Spannable str = getText();
|
|
|
StrikethroughSpan[] spans = str.getSpans(start, end, StrikethroughSpan.class);
|
|
|
|
|
|
for (StrikethroughSpan span : spans) {
|
|
|
str.removeSpan(span);
|
|
|
}
|
|
|
|
|
|
str.setSpan(new StrikethroughSpan(), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 应用下划线样式
|
|
|
public void applyUnderline() {
|
|
|
int start = getSelectionStart();
|
|
|
int end = getSelectionEnd();
|
|
|
|
|
|
if (start >= 0 && end >= 0 && start != end) {
|
|
|
Spannable str = getText();
|
|
|
UnderlineSpan[] spans = str.getSpans(start, end, UnderlineSpan.class);
|
|
|
|
|
|
for (UnderlineSpan span : spans) {
|
|
|
str.removeSpan(span);
|
|
|
}
|
|
|
|
|
|
str.setSpan(new UnderlineSpan(), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
|
|
|
}
|
|
|
}
|
|
|
} |