注:このバグhereのreproを含むGitHubレポを作成しました。クローンを作成してアプリを自分自身で試してみて、バグを確認してください。関連コードはhereです:コメント部分にはコメントが残っていますが、コメントを外してコメントを残すと、そのバグが発生します。EditTextでカスタムEditableを使用すると、編集中にエラーが発生する
私は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
など、SSBではなくreturn this
など)をSSBに転送するだけです。今
私はEditText
を持っていると私は編集したとき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
の基本となるテキストとしてColoredText
を設定してみてください。上の例では、最初の行の任意の部分をタップしてHello world!
とし、ランダムな文字を入力し始めます。 2行目が影響を受け、何らかの形で(改行や矢印キーに触れていなくても)、カーソルは最終的に2行目に流出します。入力した文字の中には、カーソルが移動しても表示されないものがあります。
ここでsetEditableFactory
の部分をコメントアウトして、setText()
の間にテキストがSSBにコピーされ、再度アプリケーションを実行すると、不具合がないことがわかります。
扱うときにそれも、あなたがそのままsetEditableFactory
一部を残す場合は動作しますが、
SpannableStringBuilder text = new SpannableStringBuilder("Hello world!\nHello world again!");
でtext
の変数の初期化を置き換え、setText()
は、それがどんなEditable
を受け入れるだろうと言うが、それはうまく動作しません。 SSB以外のものでなぜこれが起こり、どのように修正できますか?ありがとう。
私は、 'SpannableStringBuilder'のソースコードを掘り下げて、' Editable'などのインターフェースで定義された責任を果たすだけでなく、 'SpanWatcher.onSpanChanged()'を 'この '。'EditText'の実際の仕事場である' DynamicLayout'は、渡された参照のメンバー(実際の 'ColoredSpan'インスタンスです)と等しいかどうかをチェックすることによって' onSpanChanged() 'に応答します。明らかに彼らは異なっており、私はこれが問題であると思われます。 –
実際には 'SpannableStringBuilder'は' Editable'だけではありません。カスタムの 'Editable'サブクラスが必要な場合は、' SpannableStringBuilder'が動作するかもしれません。しかし、私はこれらのことについてはあまりよく分からないので、コメントとして投稿します。 –
@DurgadassS問題を掘り下げて解決策を提供してくれてありがとう。私はSSBを拡張しようとしましたが、今は完全に動作しています。あなたが回答を投稿すると、私はそれを喜んで受け入れるでしょう。 –