說明:我創建了一個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,現在應用程序完美工作。如果您發佈答案,我會很樂意接受。 –