2017-07-16 40 views
0

说明:我创建了一个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,并且其所有方法调用(除了appenddelete等等,其中return this代替SSB)简单地转发给SSB。

现在,当我有一个EditText,我尝试设置ColoredTextEditText的基础文本,像这样

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之外的其他任何东西。为什么会发生这种情况,我该如何解决?谢谢。

+0

通过挖掘'SpannableStringBuilder'的源代码,我发现它不仅完成了Interfaces'Editable'等定义的职责,还通过调用'SpanWatcher.onSpanChanged()'来报告跨度变化, this'。'DynamicLayout'(EditText'的真正主力)通过检查引用传递给它的成员(这是我们实际的'ColoredSpan'实例)的等式来响应'onSpanChanged()'。显然他们是不同的,我怀疑这是一个问题。 –

+0

实际上'SpannableStringBuilder'不只是'可编辑的,但更多。如果你需要一个自定义的'Editable'子类,'SpannableStringBuilder'可以工作。不过,我对这些事情不是很确定,因此我把它作为评论发布。 –

+0

@DurgadassS非常感谢,无论是挖掘问题还是提供解决方案。我试图扩展SSB,现在应用程序完美工作。如果您发布答案,我会很乐意接受。 –

回答

2

挖的SpannableStringBuilder源代码,我想通了,它不仅满足了在接口Editable等定义的职责也由经过this调用SpanWatcher.onSpanChanged()报告的跨度变化。 DynamicLayoutEditText的真正主力)通过检查与它的成员(这是我们的实际ColoredSpan实例)相关的参考传递的相等性来响应onSpanChanged()。显然他们是不同的,我怀疑这是一个问题。其实SpannableStringBuilder不只是Editable,但更多。如果您需要定制Editable子类SpannableStringBuilder可能工作。

相关问题