如何防止删除 EditText 中的 spannable
How to prevent deleting a spannable in an EditText
有没有办法阻止用户删除或修改 EditText 中的 spannable?更具体地说,我有一个 ImageSpan 作为 EditText 的第一个字符。我想确保用户无法删除那个 ImageSpan。
我意识到我可以使用 TextWatcher 并在用户删除 ImageSpan 时替换它。这太丑陋了,我希望首先有一种方法可以防止删除。
这是我设置文本值的代码片段:
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);
好的,这个解决方案相当复杂,但它确实有效。我们需要一个自定义 InputFilter
和一个 SpanWatcher
。开始吧。
第一步很简单。我们设置一个不可编辑的带有图片跨度的前缀,并在这个前缀之后设置一个光标。
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());
然后我们设置一个 InputFilter
来防止带有 span 的前缀被编辑。可以通过移动正在编辑的范围使其在前缀之后开始来完成。
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;
}
}
}
});
然后我们创建一个 SpanWatcher
来检测选择变化并将选择移出前缀范围。
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);
}
}
}
};
最后我们将 SpanWatcher
添加到文本中。
edit.getText().setSpan(watcher, 0, 0, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
仅此而已。因此,将此解决方案与仅添加 TextWatcher
进行比较,我更喜欢后一种方法。
有没有办法阻止用户删除或修改 EditText 中的 spannable?更具体地说,我有一个 ImageSpan 作为 EditText 的第一个字符。我想确保用户无法删除那个 ImageSpan。
我意识到我可以使用 TextWatcher 并在用户删除 ImageSpan 时替换它。这太丑陋了,我希望首先有一种方法可以防止删除。
这是我设置文本值的代码片段:
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);
好的,这个解决方案相当复杂,但它确实有效。我们需要一个自定义 InputFilter
和一个 SpanWatcher
。开始吧。
第一步很简单。我们设置一个不可编辑的带有图片跨度的前缀,并在这个前缀之后设置一个光标。
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());
然后我们设置一个 InputFilter
来防止带有 span 的前缀被编辑。可以通过移动正在编辑的范围使其在前缀之后开始来完成。
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;
}
}
}
});
然后我们创建一个 SpanWatcher
来检测选择变化并将选择移出前缀范围。
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);
}
}
}
};
最后我们将 SpanWatcher
添加到文本中。
edit.getText().setSpan(watcher, 0, 0, Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
仅此而已。因此,将此解决方案与仅添加 TextWatcher
进行比较,我更喜欢后一种方法。