web-dev-qa-db-fra.com

Comment puis-je modifier le texte EditText sans déclencher l'observateur de texte?

J'ai un champ EditText avec un observateur de texte client dessus. Dans un morceau de code, je dois changer la valeur dans le EditText que je fais en utilisant .setText("whatever").

Le problème est que dès que je fais ce changement, la méthode afterTextChanged est appelée, ce qui a créé une boucle infinie. Comment puis-je changer le texte sans qu'il déclenche afterTextChanged?

J'ai besoin du texte dans la méthode afterTextChanged, donc ne suggérez pas de supprimer le TextWatcher.

82
user1143767

Vous pouvez annuler l'enregistrement de l'observateur, puis le réenregistrer.

Alternativement, vous pouvez définir un indicateur pour que votre observateur sache quand vous venez de modifier le texte vous-même (et doit donc l'ignorer).

60
CasualT

Vous pouvez vérifier quelle vue a actuellement le focus pour faire la distinction entre les événements déclenchés par l'utilisateur et le programme.

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

myEditText.addTextChangedListener(new TextWatcher() {

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

    //...
});

Edit: Comme LairdPleng correctement mentionné, cela ne fonctionne pas si le myEditText a déjà le focus et que vous modifier par programme le texte. Donc, avant d'appeler myEditText.setText(...), vous devez appeler myEditText.clearFocus() comme Chack said, ce qui résout également ce problème.

88
Willi Mentzel
public class MyTextWatcher implements TextWatcher {
    private EditText et;

    // Pass the EditText instance to TextWatcher by constructor
    public MyTextWatcher(EditText et) {
        this.et = et;
    }

    @Override
    public void afterTextChanged(Editable s) {
        // Unregister self before update
        et.removeTextChangedListener(this);

        // The trick to update text smoothly.
        s.replace(0, s.length(), "text");

        // Re-register self after update
        et.addTextChangedListener(this);
    }
}

Usage:

et_text.addTextChangedListener(new MyTextWatcher(et_text));

Vous pouvez ressentir un peu de retard lors de la saisie rapide de texte si vous utilisez editText.setText () au lieu de editable.replace ().

24
Chan Chun Him

Astuce facile à corriger ... tant que votre logique pour dériver la nouvelle valeur de texte d'édition est idempotente (ce qu'elle serait probablement, mais juste en disant). Dans votre méthode d'écoute, modifiez le texte d'édition uniquement si la valeur actuelle est différente de la dernière fois que vous avez modifié la valeur.

par exemple.,

TextWatcher tw = new TextWatcher() {
  private String lastValue = "";

  @Override
  public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
  }

  @Override
  public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
  }

  @Override
  public void afterTextChanged(Editable editable) {

    // Return value of getNewValue() must only depend
    // on the input and not previous state
    String newValue = getNewValue(editText.getText().toString());
    if (!newValue.equals(lastValue)) {
      lastValue = newValue;

      editText.setText(newValue);
    }
  }
};
14
Jeffrey Blattman

J'utilise de cette façon:

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);
                }
            }
        });

Et chaque fois que vous devez modifier le texte par programme, effacez d'abord le focus

mEditText.clearFocus();
mEditText.setText(lastAddress.complement);
3
Arthur Melo

Salut, si vous devez rester concentré sur EditText changer le texte, vous pouvez demander le focus. Cela a fonctionné pour moi:

if (getCurrentFocus() == editText) {
    editText.clearFocus();
    editText.setText("...");
    editText.requestFocus();
}
3
Milos Simic Simo

Vous pouvez utiliser la syntaxe DSL de Kotlin pour avoir la solution générique pour cela:

fun TextView.applyWithDisabledTextWatcher(textWatcher: TextWatcher, codeBlock: TextView.() -> Unit) {
    this.removeTextChangedListener(textWatcher)
    codeBlock()
    this.addTextChangedListener(textWatcher)
}

Et à l'intérieur de votre TextWatcher, vous pouvez l'utiliser comme:

editText.applyWithDisabledTextWatcher(this) {
    text = formField.name
}
2
Alex Misiulia

essayez cette logique: je voulais setText ("") sans aller à la boucle infinie et ce code fonctionne pour moi. J'espère que vous pourrez modifier cela pour répondre à vos besoins

        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
        }
    });
1
ShAkKiR

Cela fonctionne bien pour moi

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) { }
    });
1
user3361395

Voici une classe pratique qui fournit une interface plus simple que TextWatcher pour le cas normal de vouloir voir les changements lorsqu'ils se produisent. Il permet également d'ignorer la prochaine modification lorsque l'OP le demande.

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());
            }
        }
    }
}

Utilisez-le comme ceci:

EditTexts.EditTextChangeListener listener = new EditTexts.EditTextChangeListener(s -> doSomethingWithString(s));
editText.addTextChangedListener(listener);

Chaque fois que vous souhaitez modifier le contenu de editText sans provoquer une cascade de modifications récursives, procédez comme suit:

listener.ignoreNextChange();
editText.setText("whatever"); // this won't trigger the listener
1
JohnnyLambada

Le problème peut être facilement résolu en utilisant tag déposé et vous n'avez même pas à gérer le focus de editText.

Définition du texte et de la balise par programme

editText.tag = "dummyTag"
editText.setText("whatever")
editText.tag = null

Vérification de la tag dans onTextChanged

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

Ma variante:

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);
            }
        });
    }


}

Définissez les écouteurs uniquement à l'aide de setOnTextChangeListener () et définissez le texte uniquement à l'aide de setNewText (je voulais remplacer setText (), mais c'est définitif)

0
kandi

J'ai créé une classe abstraite qui atténue le problème cyclique de quand une modification de EditText est effectuée via un TextWatcher.

/**
 * 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);
}
0
Eurig Jones