web-dev-qa-db-fra.com

android EditText - événement de saisie terminé

Je veux attraper un événement lorsque l'utilisateur a fini de modifier EditText.

Comment ceci peut être fait?

93
eddyuk

Lorsque l'utilisateur aura terminé sa modification, il appuiera sur Done ou Enter

((EditText)findViewById(R.id.youredittext)).setOnEditorActionListener(
    new EditText.OnEditorActionListener() {
        @Override
        public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
            if (actionId == EditorInfo.IME_ACTION_SEARCH ||
                    actionId == EditorInfo.IME_ACTION_DONE ||
                    event != null &&
                    event.getAction() == KeyEvent.ACTION_DOWN &&
                    event.getKeyCode() == KeyEvent.KEYCODE_ENTER) {
                if (event == null || !event.isShiftPressed()) {
                   // the user is done typing. 

                   return true; // consume.
                }                
            }
            return false; // pass on to other listeners. 
        }
    }
);
103
Reno

Mieux, vous pouvez également utiliser le programme d'écoute EditText onFocusChange pour vérifier si l'utilisateur a effectué les modifications: (Ne pas compter sur l'utilisateur en appuyant sur le bouton Terminé ou Entrée du clavier virtuel)

 ((EditText)findViewById(R.id.youredittext)).setOnFocusChangeListener(new OnFocusChangeListener() {

    @Override
    public void onFocusChange(View v, boolean hasFocus) {
    /* When focus is lost check that the text field
    * has valid values.
    */
      if (!hasFocus) {
       validateInput(v);
      }
    }
 });
160

Personnellement, je préfère l'envoi automatique après la fin de la saisie. Voici comment vous pouvez détecter cet événement.

Déclarations et initialisation:

private Timer timer = new Timer();
private final long DELAY = 1000; // in ms

Auditeur dans par exemple onCreate ()

EditText editTextStop = (EditText) findViewById(R.id.editTextStopId);
    editTextStop.addTextChangedListener(new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence s, int start, int count,
                int after) {
        }
        @Override
        public void onTextChanged(final CharSequence s, int start, int before,
                int count) {
            if(timer != null)
                timer.cancel();
        }
        @Override
        public void afterTextChanged(final Editable s) {
            //avoid triggering event when text is too short
            if (s.length() >= 3) {              

                timer = new Timer();
                timer.schedule(new TimerTask() {
                    @Override
                    public void run() {
                        // TODO: do what you need here (refresh list)
                        // you will probably need to use
                        // runOnUiThread(Runnable action) for some specific
                        // actions
                        serviceConnector.getStopPoints(s.toString());
                    }

                }, DELAY);
            }
        }
    });

Ainsi, lorsque le texte est modifié, le minuteur commence à attendre que les modifications suivantes soient apportées. Quand ils se produisent, la minuterie est annulée puis redémarrée.

45
ZimaXXX

Vous pouvez le faire en utilisant setOnKeyListener ou en utilisant un textWatcher comme:

Définir l'observateur du texte editText.addTextChangedListener(textWatcher);

alors appelez

private TextWatcher textWatcher = new TextWatcher() {

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

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

        @Override
        public void afterTextChanged(Editable s) {

        }
    };
19
ShineDown

@Reno et @ Vinayak B répondent ensemble si vous souhaitez masquer le clavier après l'action

textView.setOnEditorActionListener(new EditText.OnEditorActionListener() {
    @Override
    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
        if (actionId == EditorInfo.IME_ACTION_SEARCH || actionId == EditorInfo.IME_ACTION_DONE) {
            InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
            imm.hideSoftInputFromWindow(textView.getWindowToken(), 0);
            return true;
        }
        return false;
    }
});

textView.setOnFocusChangeListener(new View.OnFocusChangeListener() {
    @Override
    public void onFocusChange(View v, boolean hasFocus) {
        if (!hasFocus) {
             // your action here
        }
    }
});
4
Tyler Davis

Bien que de nombreuses réponses pointent dans la bonne direction, je pense qu'aucune d'entre elles ne répond à ce que l'auteur de la question pensait. Ou du moins j'ai compris la question différemment parce que je cherchais une réponse à un problème similaire. Le problème est "Comment savoir quand l'utilisateur arrête de taper sans qu'il appuie sur un bouton" et déclenche une action (par exemple, l'auto-complétion). Si vous voulez le faire, démarrez le minuteur dans onTextChanged avec un délai que vous considèreriez comme utilisateur arrêté de taper (par exemple 500-700ms), pour chaque nouvelle lettre au démarrage, annulez la précédente signaler que quand ils coche ils ne font rien). Voici un code similaire à ce que j'ai utilisé:

new Timer().schedule(new TimerTask() {
  @Override
  public void run() {
     if (!running) {                            
        new DoPost().execute(s.toString());
  });
 }
}, 700);

Notez que je modifie l'indicateur booléen en cours d'exécution dans ma tâche asynchrone (Task obtient le JSON du serveur pour la saisie automatique).

N'oubliez pas non plus que cela crée de nombreuses tâches de minuterie (je pense qu'elles sont programmées sur le même thread que vous devriez vérifier), il y a probablement de nombreux points à améliorer, mais cette approche fonctionne également. utiliser une minuterie puisqu'il n'y a pas d '"événement d'arrêt de la saisie de l'utilisateur"

4
PSIXO

Une approche différente ... voici un exemple:.

 myEditText.addTextChangedListener(new TextWatcher() {
             
            private String s;
            private long after;
			private Thread t;
            private Runnable runnable_EditTextWatcher = new Runnable() {
                @Override
                public void run() {
                    while (true) {
                        if ((System.currentTimeMillis() - after) > 600)
                        {
                            Log.d("Debug_EditTEXT_watcher", "(System.currentTimeMillis()-after)>600 ->  " + (System.currentTimeMillis() - after) + " > " + s);
                            // Do your stuff
                            t = null;
                            break;
                        }
                    }
                }
            };
            
            @Override
            public void onTextChanged(CharSequence ss, int start, int before, int count) {
                s = ss.toString();
            }
            
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            }
            
            @Override
            public void afterTextChanged(Editable ss) {
                after = System.currentTimeMillis();
                if (t == null)
                {
                    t = new Thread(runnable_EditTextWatcher);
                      t.start();
                }
            }
        });

2
ChristianMEC

Ok, cela fonctionnera à 100% à coup sûr.

Vous devez d’abord configurer l’écouteur si le clavier est affiché ou masqué . Si le clavier est affiché, l’utilisateur est probablement en train de taper, sinon vous tapez.

final View activityRootView = findViewById(Android.R.id.content);
        activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {

                    Rect r = new Rect();
                    //r will be populated with the coordinates of your view that area still visible.
                    activityRootView.getWindowVisibleDisplayFrame(r);

                    int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
                    if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...

                        isTyping = true;
                    } else {
//to make sure this will call only once when keyboard is hide.
                        if(isTyping){
                            isTyping = false;
                        }
                    }
            }
        });
2
Krit

J'ai fait quelque chose comme cette classe abstraite qui peut être utilisée à la place du type TextView.OnEditorActionListener. 

abstract class OnTextEndEditingListener : TextView.OnEditorActionListener {

    override fun onEditorAction(textView: TextView?, actionId: Int, event: KeyEvent?): Boolean {

        if(actionId == EditorInfo.IME_ACTION_SEARCH ||
                actionId == EditorInfo.IME_ACTION_DONE ||
                actionId == EditorInfo.IME_ACTION_NEXT ||
                event != null &&
                event.action == KeyEvent.ACTION_DOWN &&
                event.keyCode == KeyEvent.KEYCODE_ENTER) {

            if(event == null || !event.isShiftPressed) {
                // the user is done typing.
                return onTextEndEditing(textView, actionId, event)
            }
        }
        return false // pass on to other listeners
    }

    abstract fun onTextEndEditing(textView: TextView?, actionId: Int, event: KeyEvent?) : Boolean
}
0
Michał Ziobro

J'avais le même problème et je ne voulais pas que l'utilisateur appuie sur Done ou Enter. 

Ma première tentative a été d'utiliser l'écouteur onFocusChange, mais il s'est avéré que mon EditText avait le focus par défaut. Lorsque l'utilisateur a appuyé sur une autre vue, onFocusChange a été déclenché sans que l'utilisateur lui ait attribué le focus.

La solution suivante l'a fait pour moi, où onFocusChange est attaché si l'utilisateur a touché le EditText:

final myEditText = new EditText(myContext); //make final to refer in onTouch
myEditText.setOnTouchListener(new OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {
            myEditText.setOnFocusChangeListener(new OnFocusChangeListener() {

                @Override
                public void onFocusChange(View v, boolean hasFocus) {
                    if(!hasFocus){
                        // user is done editing
                    }
                }
            }
        }
}

Dans mon cas, lorsque l'utilisateur a terminé l'édition, l'écran a été rendu, renouvelant ainsi l'objet myEditText. Si le même objet est conservé, vous devriez probablement supprimer l'écouteur onFocusChange dans onFocusChange pour éviter le problème onFocusChange décrit au début de cet article.

0
Jos

J'ai eu le même problème en essayant d'implémenter "maintenant en tapant" sur l'application de chat . Essayez d'étendre EditText comme suit:

public class TypingEditText extends EditText implements TextWatcher {

private static final int TypingInterval = 2000;


public interface OnTypingChanged {
    public void onTyping(EditText view, boolean isTyping);
}
private OnTypingChanged t;
private Handler handler;
{
    handler = new Handler();
}
public TypingEditText(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    this.addTextChangedListener(this);
}

public TypingEditText(Context context, AttributeSet attrs) {
    super(context, attrs);
    this.addTextChangedListener(this);
}

public TypingEditText(Context context) {
    super(context);
    this.addTextChangedListener(this);
}

public void setOnTypingChanged(OnTypingChanged t) {
    this.t = t;
}

@Override
public void afterTextChanged(Editable s) {
    if(t != null){
        t.onTyping(this, true);
        handler.removeCallbacks(notifier);
        handler.postDelayed(notifier, TypingInterval);
    }

}

private Runnable notifier = new Runnable() {

    @Override
    public void run() {
        if(t != null)
            t.onTyping(TypingEditText.this, false);
    }
};

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


@Override
public void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) { }

}

0
Nadav

Je l'ai terminée avec le même problème et je ne pouvais pas utiliser la solution avec onEditorAction ou onFocusChange et je ne voulais pas essayer le minuteur. Une minuterie est trop dangereuse pour le goût, à cause de tous les threads et trop imprévisible, car vous ne savez pas quand votre code est exécuté.

Le onEditorAction ne capture pas lorsque l'utilisateur quitte sans utiliser un bouton et si vous l'utilisez, notez que KeyEvent peut être nul. Le focus n'est pas fiable aux deux extrémités, l'utilisateur peut obtenir le focus et le quitter sans entrer de texte ni sélectionner le champ et l'utilisateur n'a pas besoin de quitter le dernier champ EditText.

Ma solution utilise onFocusChange et un indicateur défini lorsque l'utilisateur commence à modifier du texte, ainsi qu'une fonction permettant d'obtenir le texte de la dernière vue active, que j'appelle en cas de besoin.

Je supprime simplement le focus sur tous mes champs de texte pour rendre plus délicat le code en mode texte, le code clearFocus n'est exécuté que si le champ a le focus. J'appelle la fonction dans onSaveInstanceState afin que je n'ai pas à enregistrer l'indicateur (mEdit) en tant qu'état de la vue EditText et lorsque des boutons importants sont cliqués et lorsque l'activité est fermée. 

Soyez prudent avec TexWatcher car il est souvent nécessaire d’utiliser la condition focus pour ne pas réagir lorsque le code onRestoreInstanceState est entré dans le texte . I 

final EditText mEditTextView = (EditText) getView();

    mEditTextView.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 (!mEditing && mEditTextView.hasFocus()) {
                mEditing = true;
            }
        }
    });
    mEditTextView.setOnFocusChangeListener(new View.OnFocusChangeListener() {

        @Override
        public void onFocusChange(View v, boolean hasFocus) {
            if (!hasFocus && mEditing) {
                mEditing = false;
                ///Do the thing
            }
        }
    });
protected void saveLastOpenField(){
    for (EditText view:getFields()){
            view.clearFocus();
    }
}
0
Rezder