Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/214.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何在不触发文本监视程序的情况下更改EditText文本?_Java_Android_Event Handling_Infinite Loop - Fatal编程技术网

Java 如何在不触发文本监视程序的情况下更改EditText文本?

Java 如何在不触发文本监视程序的情况下更改EditText文本?,java,android,event-handling,infinite-loop,Java,Android,Event Handling,Infinite Loop,我有一个EditText字段,上面有一个客户文本观察者。在一段代码中,我需要更改EditText中的值,我使用.setText(“任意”) 问题是,只要我做出更改,就会调用posterextchanged方法,该方法创建了一个无限循环。如何在不触发后文本更改的情况下更改文本 我需要后文本更改方法中的文本,因此不要建议删除TextWatcher您可以注销该观察者,然后重新注册它 或者,您可以设置一个标志,以便您的观察者知道您自己何时刚刚更改了文本(因此应该忽略它)。简单的修复技巧。。。只要你的逻辑

我有一个
EditText
字段,上面有一个客户文本观察者。在一段代码中,我需要更改EditText中的值,我使用
.setText(“任意”)

问题是,只要我做出更改,就会调用
posterextchanged
方法,该方法创建了一个无限循环。如何在不触发后文本更改的情况下更改文本


我需要后文本更改方法中的文本,因此不要建议删除
TextWatcher

您可以注销该观察者,然后重新注册它

或者,您可以设置一个标志,以便您的观察者知道您自己何时刚刚更改了文本(因此应该忽略它)。

简单的修复技巧。。。只要你的逻辑推导出新的编辑文本值是幂等的(它可能是,但只是说)。在侦听器方法中,仅当当前值与上次修改值不同时才修改编辑文本

e、 g


您应该确保文本更改的实现是稳定的,并且在不需要更改的情况下不会更改文本。通常,这将是任何内容已经通过了一次观察者

最常见的错误是在关联的EditText或可编辑文本中设置新文本,即使文本没有实际更改

最重要的是,如果您对可编辑视图(而不是特定视图)进行更改,您可以轻松地恢复监视程序,还可以使用一些单元测试对其进行单独测试,以确保其具有您想要的结果

由于Editable是一个接口,您甚至可以使用它的虚拟实现,在测试应该稳定的内容时,如果调用它的任何方法试图更改其内容,就会引发RuntimeException。

My variant:

public class CustomEditText extends AppCompatEditText{
    TextWatcher l;

    public CustomEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
    public void setOnTextChangeListener(TextWatcher l) {
        try {
            removeTextChangedListener(this.l);
        } catch (Throwable e) {}
        addTextChangedListener(l);
        this.l = l;
    }

    public void setNewText(CharSequence s) {
        final TextWatcher l = this.l;
        setOnTextChangeListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });
        setText(s);
        post(new Runnable() {
            @Override
            public void run() {
                setOnTextChangeListener(l);
            }
        });
    }


}
仅使用SetContextChangeListener()设置侦听器,并仅使用setNewText设置文本(我想覆盖setText(),但它是最终设置)

尝试以下逻辑: 我想在不进入无限循环的情况下设置text(“”),这段代码适合我。我希望你能修改这个以符合你的要求

        final EditText text= (EditText)findViewById(R.id.text);
        text.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        }
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) {

        }
        @Override
        public void afterTextChanged(Editable s) {
            if(s.toString().isEmpty())return;
            text.setText("");
            //your code
        }
    });
简短回答 您可以检查哪个视图当前具有焦点,以区分用户和程序触发的事件

EditText myEditText = (EditText) findViewById(R.id.myEditText);

myEditText.addTextChangedListener(new TextWatcher() {

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        if (myEditText.hasFocus()) {
            // is only executed if the EditText was directly changed by the user
        }
    }

    //...
});
长话短说 作为简短回答的补充: 如果以编程方式更改文本时,
myEditText
已经具有焦点,则应调用
clearFocus()
,然后调用
setText(…)
,然后重新请求焦点。最好将其放在效用函数中:

void updateText(EditText editText, String text) {
    boolean focussed = editText.hasFocus();
    if (focussed) {
        editText.clearFocus();
    }
    editText.setText(text);
    if (focussed) {
        editText.requestFocus();
    }
}
对于科特林:

由于Kotlin支持扩展函数,因此实用程序函数可能如下所示:

fun EditText.updateText(text: String) {
    val focussed = hasFocus()
    if (focussed) {
        clearFocus()
    }
    setText(text)
    if (focussed) {
        requestFocus()
    }
}
EditTexts.EditTextChangeListener listener = new EditTexts.EditTextChangeListener(s -> doSomethingWithString(s));
editText.addTextChangedListener(listener);
爪哇:

科特林:

class MyTextWatcher(private val editText: EditText) : TextWatcher {

  override fun afterTextChanged(e: Editable) {
    editText.removeTextChangedListener(this)
    e.replace(0, e.length, e.toString())
    editText.addTextChangedListener(this)
  }

  ...
}
用法:

et_text.addTextChangedListener(new MyTextWatcher(et_text));

如果您使用的是editText.setText()而不是editable.replace(),我创建了一个抽象类,它可以缓解通过TextWatcher修改editText时的循环问题

/**
 * An extension of TextWatcher which stops further callbacks being called as a result of a change
 * happening within the callbacks themselves.
 */
public abstract class EditableTextWatcher implements TextWatcher {

    private boolean editing;

    @Override
    public final void beforeTextChanged(CharSequence s, int start, int count, int after) {
        if (editing)
            return;

        editing = true;
        try {
            beforeTextChange(s, start, count, after);
        } finally {
            editing = false;
        }
    }

    abstract void beforeTextChange(CharSequence s, int start, int count, int after);

    @Override
    public final void onTextChanged(CharSequence s, int start, int before, int count) {
    if (editing)
        return;

        editing = true;
        try {
            onTextChange(s, start, before, count);
        } finally {
            editing = false;
        }
    }

    abstract void onTextChange(CharSequence s, int start, int before, int count);

    @Override
    public final void afterTextChanged(Editable s) {
        if (editing)
            return;

        editing = true;
        try {
            afterTextChange(s);
        } finally {
            editing = false;
        }
    }    

    public boolean isEditing() {
        return editing;
    }

    abstract void afterTextChange(Editable s);
}

如果您需要关注
EditText
更改文本,您可以请求关注:

if (getCurrentFocus() == editText) {
    editText.clearFocus();
    editText.setText("...");
    editText.requestFocus();
}

这里有一个方便的类,它提供了一个比TextWatcher更简单的接口,用于希望在发生更改时查看更改的正常情况。它还允许在OP请求时忽略下一个更改

public class EditTexts {
    public final static class EditTextChangeListener implements TextWatcher {
        private final Consumer<String> onEditTextChanged;
        private boolean ignoreNextChange = false;
        public EditTextChangeListener(Consumer<String> onEditTextChanged){
            this.onEditTextChanged = onEditTextChanged;
        }
        public void ignoreNextChange(){
            ignoreNextChange = true;
        }
        @Override public void beforeTextChanged(CharSequence __, int ___, int ____, int _____) { }
        @Override public void onTextChanged(CharSequence __, int ___, int ____, int _____) { }
        @Override public void afterTextChanged(Editable s) {
            if (ignoreNextChange){
                ignoreNextChange = false;
            } else {
                onEditTextChanged.accept(s.toString());
            }
        }
    }
}
每当您想要修改
editText
的内容而不引起递归编辑的级联时,请执行以下操作:

listener.ignoreNextChange();
editText.setText("whatever"); // this won't trigger the listener
我这样说:

mEditText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {}

            @Override
            public void afterTextChanged(Editable s) {
                if (mEditText.isFocused()) { //<-- check if is focused 
                    mEditText.setTag(true);
                }
            }
        });

您可以使用Kotlin DSL语法为以下内容提供通用解决方案:

fun TextView.applyWithDisabledTextWatcher(textWatcher: TextWatcher, codeBlock: TextView.() -> Unit) {
    this.removeTextChangedListener(textWatcher)
    codeBlock()
    this.addTextChangedListener(textWatcher)
}
在TextWatcher中,您可以将其用作:

editText.applyWithDisabledTextWatcher(this) {
    text = formField.name
}
这对我有好处

EditText inputFileName; // = (EditText)findViewbyId(R.id...)
inputFileName.addTextChangedListener(new TextWatcher() {
        public void afterTextChanged(Editable s) {

            //unregistering for event in order to prevent infinity loop
            inputFileName.removeTextChangedListener(this);

            //changing input's text
            String regex = "[^a-z0-9A-Z\\s_\\-]";
            String fileName = s.toString();
            fileName = fileName.replaceAll(regex, "");
            s.replace(0, s.length(), fileName); //here is setting new text

            Log.d("tag", "----> FINAL FILE NAME: " + fileName);

            //registering back for text changes
            inputFileName.addTextChangedListener(this);
        }

        public void beforeTextChanged(CharSequence s, int start, int count, int after) { }

        public void onTextChanged(CharSequence s, int start, int before, int count) { }
    });

使用
tag
field可以很容易地解决这个问题,您甚至不必处理editText的焦点

以编程方式设置文本和标记

editText.tag = "dummyTag"
editText.setText("whatever")
editText.tag = null
检查onTextChanged中的
标记

override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
    if (editText.tag == null) {
       // your code
    }
}

我的做法是:

在写入段中

        EditText e_q;

        e_q = (EditText) parentView.findViewWithTag("Bla" + i);

        int id=e_q.getId();
        e_q.setId(-1);
        e_q.setText("abcd...");
        e_q.setId(id);
听众

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {

        int id = view.getId();
        if(id==-1)return;

        ....

无论如何都可以使用。

非常简单,使用此方法设置文本

void updateText(EditText et, String text) {
   if (!et.getText().toString().equals(text))
       et.setText(text);
}

这样做很容易

editText.addTextChangedListener(object : TextWatcher {

        private var isEditing = false

        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {


        }

        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {

        }

        override fun afterTextChanged(s: Editable?) {
            if(!isEditing){
                isEditing = true
                editText.setText("Hello World!")
                isEditing = false
            }

        }
    })

这样,它就不会在无限循环中存储了

为什么会被否决?这是解决我问题的完美方法,而其他方法都不起作用。不过,我要补充的是,这个解决方案不需要为TextWatcher子类。谢谢Chan Chun Hem。你关于注销和重新注册TextWatcher的想法非常有效。但是,虽然et.setText(“”)有效,但s.replace(0,s.length(),“”)无效。@stevehs17,我真的不知道您的代码如何,但用法更新得非常详细,请尝试一下。这仍然会导致该方法被调用两次,但是,否?是的,这就是为什么我声明它应该是幂等的(应该更清楚地说明这一点)。。。这并不理想,但如果您担心单个方法调用的开销不起作用,那么您必须真正进行核心优化。如果是这样的话,您可以重新组织代码,在侦听器调用
setText()
之前将其删除,并在调用之后重新添加。更干净。Kotlin DSL令人敬畏,就是这样。谢谢,不太理想。如果我想在edittext有焦点的情况下设置没有触发器侦听器的setText(),那该怎么办?有一段时间我一直在寻找它,但不是我这边的解决方案
    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {

        int id = view.getId();
        if(id==-1)return;

        ....
void updateText(EditText et, String text) {
   if (!et.getText().toString().equals(text))
       et.setText(text);
}
editText.addTextChangedListener(object : TextWatcher {

        private var isEditing = false

        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {


        }

        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {

        }

        override fun afterTextChanged(s: Editable?) {
            if(!isEditing){
                isEditing = true
                editText.setText("Hello World!")
                isEditing = false
            }

        }
    })