说明:我创建了一个GitHub回购库,其中包含此bug的代码复制here。随意克隆并自己尝试应用程序以查看错误。相关的代码是here:评论部分保留在评论中,它的工作正常,取消注释,你会遇到错误。使用自定义Editing with EditText在编辑时会出现问题
我在为Android构建一个源代码编辑器应用程序。我有一个自定义Editable
类型的包装SpannableStringBuilder
(这将从此以后将被称为SSB)。这里是它的代码:
package com.bluejay.myapplication;
import android.text.Editable;
import android.text.InputFilter;
import android.text.SpannableStringBuilder;
public class ColoredText implements Editable {
private final SpannableStringBuilder builder;
public ColoredText(String rawText) {
assert rawText != null;
this.builder = new SpannableStringBuilder(rawText);
}
@Override
public Editable replace(int st, int en, CharSequence source, int start, int end) {
this.builder.replace(st, en, source, start, end);
return this;
}
@Override
public Editable replace(int st, int en, CharSequence text) {
this.builder.replace(st, en, text);
return this;
}
@Override
public Editable insert(int where, CharSequence text, int start, int end) {
this.builder.insert(where, text, start, end);
return this;
}
@Override
public Editable insert(int where, CharSequence text) {
this.builder.insert(where, text);
return this;
}
@Override
public Editable delete(int st, int en) {
this.builder.delete(st, en);
return this;
}
@Override
public Editable append(CharSequence text) {
this.builder.append(text);
return this;
}
@Override
public Editable append(CharSequence text, int start, int end) {
this.builder.append(text, start, end);
return this;
}
@Override
public Editable append(char text) {
this.builder.append(text);
return this;
}
@Override
public void clear() {
this.builder.clear();
}
@Override
public void clearSpans() {
this.builder.clearSpans();
}
@Override
public void setFilters(InputFilter[] filters) {
this.builder.setFilters(filters);
}
@Override
public InputFilter[] getFilters() {
return this.builder.getFilters();
}
@Override
public void getChars(int start, int end, char[] dest, int destoff) {
this.builder.getChars(start, end, dest, destoff);
}
@Override
public void setSpan(Object what, int start, int end, int flags) {
this.builder.setSpan(what, start, end, flags);
}
@Override
public void removeSpan(Object what) {
this.builder.removeSpan(what);
}
@Override
public <T> T[] getSpans(int start, int end, Class<T> type) {
return this.builder.getSpans(start, end, type);
}
@Override
public int getSpanStart(Object tag) {
return this.builder.getSpanStart(tag);
}
@Override
public int getSpanEnd(Object tag) {
return this.builder.getSpanEnd(tag);
}
@Override
public int getSpanFlags(Object tag) {
return this.builder.getSpanFlags(tag);
}
@Override
public int nextSpanTransition(int start, int limit, Class type) {
return this.builder.nextSpanTransition(start, limit, type);
}
@Override
public int length() {
return this.builder.length();
}
@Override
public char charAt(int index) {
return this.builder.charAt(index);
}
@Override
public CharSequence subSequence(int start, int end) {
return this.builder.subSequence(start, end);
}
}
正如你所看到的,这种类型是SSB的简单包装。 new ColoredText(str)
从str
创建底层SSB,并且其所有方法调用(除了append
,delete
等等,其中return this
代替SSB)简单地转发给SSB。
现在,当我有一个EditText
,我尝试设置ColoredText
为EditText
的基础文本,像这样
EditText editText = (EditText) findViewById(R.id.editText);
// By default, setText() will attempt to copy the passed CharSequence into a new SSB.
// See https://github.com/android/platform_frameworks_base/blob/master/core/java/android/widget/TextView.java#L4396
// and https://github.com/android/platform_frameworks_base/blob/master/core/java/android/text/Editable.java#L143
// I want to prevent this and have the ColoredText instead of an SSB be the EditText's
// underlying text, that is, I want the mText member to be of type ColoredText.
editText.setEditableFactory(new Editable.Factory() {
@Override
public Editable newEditable(CharSequence source) {
return (Editable) source; // source is ColoredText
}
});
ColoredText text = new ColoredText("Hello world!\nHello world again!");
editText.setText(text, TextView.BufferType.EDITABLE);
的EditText
编辑时会表现得很出问题。在上面的例子中,点击第一行的任何地方Hello world!
并开始输入随机字符。第二行会受到影响,并且以某种方式(即使不触摸换行符或箭头键),光标最终会溢出到第二行。即使光标会移动,您输入的一些字符也可能不会显示。
现在,如果您注释掉setEditableFactory
部件,则在setText()
期间将文本复制到SSB中,然后再次运行该应用程序,您将看到没有毛刺。
,如果你离开setEditableFactory
部分完好,但
SpannableStringBuilder text = new SpannableStringBuilder("Hello world!\nHello world again!");
显然取代text
的变量初始化它甚至,虽然setText()
说,它会接受任何Editable
,它不能很好地工作打交道时除了SSB之外的其他任何东西。为什么会发生这种情况,我该如何解决?谢谢。
通过挖掘'SpannableStringBuilder'的源代码,我发现它不仅完成了Interfaces'Editable'等定义的职责,还通过调用'SpanWatcher.onSpanChanged()'来报告跨度变化, this'。'DynamicLayout'(EditText'的真正主力)通过检查引用传递给它的成员(这是我们实际的'ColoredSpan'实例)的等式来响应'onSpanChanged()'。显然他们是不同的,我怀疑这是一个问题。 –
实际上'SpannableStringBuilder'不只是'可编辑的,但更多。如果你需要一个自定义的'Editable'子类,'SpannableStringBuilder'可以工作。不过,我对这些事情不是很确定,因此我把它作为评论发布。 –
@DurgadassS非常感谢,无论是挖掘问题还是提供解决方案。我试图扩展SSB,现在应用程序完美工作。如果您发布答案,我会很乐意接受。 –