web-dev-qa-db-fra.com

Les événements TextWatcher sont déclenchés plusieurs fois

J'ai un problème ennuyeux avec TextWatcher. J'ai cherché sur le Web mais je n'ai rien trouvé. apprécier si quelqu'un pouvait m'aider.

Pour une raison quelconque, les appels aux événements TextWatcher lors d'un changement de texte sont irréguliers. parfois ils sont déclenchés une fois (comme ils devraient l'être), parfois deux fois, et parfois 3 fois. ne sais pas pourquoi, le tout est très simple. parfois, le paramètre Editable sur afterTextChanged () renvoie des valeurs vides dans toString () et length ().

le code est ci-dessous:

    private TextWatcher mSearchAddressTextChangeListener = new TextWatcher() {
        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count) { }

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

        @Override
        public void afterTextChanged(Editable searchedAddress) {
           System.out.println("called multiple times.");   
        }
    };

à l'intérieur de afterTextChanged() (et la AsyncTask) je ne modifie pas le texte ou la vue EditText.

j'ai vu la question posée dans les événements de TextWatcher sont appelés deux fois , mais je fais déclencher les événements plus (ou moins) plus de deux fois.

de toute façon, appréciez toute aide.

EDIT: J'ai supprimé le contenu de afterTextChanged () car ce problème se produit même sans mon code. ce qui me fait croire que c'est un bug. Le bogue se produit lorsqu'un caractère `` espace '' est entré juste après un caractère normal (les gestionnaires d'événements sont déclenchés deux fois) ou lorsqu'un caractère `` espace '' après la suppression d'un caractère normal (retour arrière. Les gestionnaires d'événements sont déclenchés 3 fois). l'aide sera toujours appréciée.

41
AsafK

J'ai eu le même genre de problème, lorsque j'ai appuyé sur la touche retour arrière avec le curseur à la fin d'un texte continu, afterTextChange a été appelé 3 fois: - La première fois avec la valeur s correcte - La deuxième fois avec une valeur claire - La troisième fois avec la valeur correcte à nouveau

Après avoir beaucoup cherché sur le Web, j'ai essayé de changer mon InputType EditText en

Android:inputType="textNoSuggestions"

Ne me demandez pas pourquoi, mais cela a fonctionné, afterTextChanged n'est désormais appelé qu'une seule fois.

54
Nico

Selon les pages de développeur pour TextWatcher , si une modification est apportée au Editable dans TextWatcher, cela déclenchera d'autres appels à tous les TextWatchers lié à ce Editable. Maintenant, clairement votre code ne déclenche pas ce comportement.

Cependant, il est fort possible que si, pour une raison quelconque, le système a un TextWatcher sur le Editable, la situation que vous décrivez peut se produire. "Pourquoi", je vous entends pleurer, "cela devrait-il arriver?"

Tout d'abord, la défense classique: il n'y a aucune raison pour que cela ne se produise pas et, strictement, le code de l'application doit être écrit pour y résister.

Deuxièmement, je ne peux pas le prouver, mais je pourrais bien imaginer que le code qui gère la mise en page du texte affiché dans un EditText utilise un TextWatcher pour gérer la mise à jour de l'affichage du texte sur le écran. Ce code peut insérer des codes de contrôle (qui ne vous sont pas affichés) dans le Editable pour garantir de bons sauts de ligne, etc. Il peut même faire plusieurs fois le tour d'une boucle pour bien faire les choses, et vous pourriez ne recevoir votre premier appel qu'après avoir effectué toutes ses ...

ÉDITER

Selon le commentaire de @Learn OpenGL ES, l'appel d'un TextWatcher serait normal pour des choses comme la correction automatique.

8
Neil Townsend
boolean isOnTextChanged = false;

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

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

@Override
public void afterTextChanged(Editable quantity) {
    if (isOnTextChanged) {
        isOnTextChanged = false;
       //dosomething
    }
5
velraj

J'ai essayé toutes les réponses à cette question, aucune d'entre elles n'a fonctionné pour moi. Mais après quelques recherches, j'ai trouvé this post. Utiliser le RxJava pour faire le debounce a bien fonctionné pour moi. Voici ma solution finale:

Ajoutez les dépendances RxJava dans le fichier Gradle:

compile 'io.reactivex:rxandroid:1.0.1'
compile 'io.reactivex:rxjava:1.0.14'
compile 'com.artemzin.rxjava:proguard-rules:1.0.14.2'

Implémentez votre sujet:

PublishSubject<String> yourSubject = PublishSubject.create();
    yourSubject .debounce(100, TimeUnit.MILLISECONDS)
            .onBackpressureLatest()
            .subscribe(s -> {
                //Implements anything you want
            });

Utilisez votre sujet dans votre TextWatcher:

TextWatcher myTextWatcher = 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) {
        yourSubject.onNext(s.toString()); //apply here your subject
    }

    @Override
    public void afterTextChanged(Editable s) {
    }
};

Ajoutez le TextWatcher dans l'écouteur EditText:

my_edit_text.addTextChangedListener(myTextWatcher);
2
Ângelo Polotto

vous pouvez utiliser une vérification booléenne, comme:

    inputBoxNumberEt.addTextChangedListener(new TextWatcher() {

        boolean ignoreChange = false;

        @Override
        public void afterTextChanged(Editable s) {
        }

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

        @Override
        public void onTextChanged(CharSequence s, int start,
                                  int before, int count) {
            if (!ignoreChange) {
              ///Do your checks                    
                ignoreChange = true;
                inputBoxNumberEt.setText(string);
                inputBoxNumberEt.setSelection(inputBoxNumberEt.getText().length());
                ignoreChange = false;
            }
        }
    });
2
M. Usman Khan

Pour moi, le code ci-dessous a fonctionné. Veuillez noter que la valeur booléenne a été modifiée après if boucle de condition dans la méthode afterTextChanged

        edittext.addTextChangedListener(new TextWatcher() 
        {
            boolean considerChange = false;

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

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

            public void afterTextChanged(Editable editable) 
            {
                if (considerChange) 
                { 
                    // your code here
                }
                considerChange = !considerChange; //see that boolean value is being changed after if loop
            }


        });
1
shankar_vl

Mon problème était lié à cela car j'exécutais mon application dans un émulateur et tapais sur le clavier de l'ordinateur portable ( sans utiliser celui de l'émulateur ) et l'étrange l'entrée serait dupliquée.

Cela ne s'est pas produit dans un appareil physique.

Suite à l'inspiration de Nico's réponse ci-dessus, c'est ce qui a fonctionné pour moi pour que le texte d'entrée modifié ne soit déclenché qu'une seule fois.

Ajout de la configuration de type d'entrée suivante:

1) À travers un style

<!-- in a style -->
<style name="My.Text.Style">
 <item name="Android:inputType">text|textMultiLine|textVisiblePassword</item>
</style>

puis utilisé en xml comme:

<EditText
   Android:id="@+id/my_edit_text"
   Android:layout_width="wrap_content"
   Android:layout_height="wrap_content"
   style="@style/My.Text.Style"
   />

[~ # ~] ou [~ # ~]

2) Directement dans le xml

<EditText
   Android:id="@+id/my_edit_text"
   Android:inputType="text|textMultiLine|textVisiblePassword"
   />

Les deux façons de le faire devraient être bien, le style était juste pour le cas où l'on veut le réutiliser sur d'autres écrans.

0
GuilhermeM