使用自定义Editable with EditText会使编辑时出现故障

时间:2017-07-16 06:30:36

标签: java android android-edittext textview spannablestring

注意:我已经创建了一个包含此错误here的repro的GitHub存储库。随意克隆并自己尝试应用程序以查看错误。相关代码为here:评论部分保留在评论中,它可以正常工作,取消注释,您将体验到该错误。

我正在为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而不是EditText SSB)简单地转发到SSB。

现在当我有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()部分,那么在setEditableFactory期间将文本复制到SSB中,然后再次运行应用程序,您将看到没有任何故障。

如果您保持text部分完好无损,但是将SpannableStringBuilder text = new SpannableStringBuilder("Hello world!\nHello world again!"); 的变量初始化替换为

,它甚至可以正常工作
setText()

显然,尽管Editable表示它会接受任何from selenium import webdriver from selenium.webdriver.common.keys import Keys kw = input("enter the keyword to search on google") # creates firefox session driver = webdriver.Firefox(executable_path=r'C:\Users\Web\Desktop\geckodriver-v0.18.0-win32\geckodriver.exe') driver.implicitly_wait(30) # navigate to google driver.get("http://www.google.com") #get the search textfield search_field = driver.find_element_by_id("lst-ib") search_field.clear() #enter search kw and submit search_field.send_keys(kw) search_field.submit() lists = driver.find_element_by_class_name("_Rm") print ("Found " + str(len(lists)) + "searches:") i=0 for listitem in lists: print(listitem) i=i+1 if(i>10): break driver.quit() ,但在处理SSB以外的任何内容时效果都不佳。为什么会发生这种情况,我该如何解决?感谢。

1 个答案:

答案 0 :(得分:2)

通过挖掘SpannableStringBuilder的源代码,我发现它不仅履行了接口Editable等定义的职责,而且还通过调用SpanWatcher.onSpanChanged()来报告跨度变化。通过thisDynamicLayoutEditText的真正主力)通过检查传入引用与其成员(这是我们实际的onSpanChanged()实例)的相等性来响应ColoredSpan 。显然他们是不同的,我怀疑这是一个问题。

实际上SpannableStringBuilder不只是Editable,而是更多。如果您需要自定义Editable子类SpannableStringBuilder可能有效。