android EditText - evento de digitação concluído

122

Quero capturar um evento quando o usuário terminar de editar o EditText.

Como pode ser feito?

Eddyuk
fonte
1
@PadmaKumar A maneira mais natural seria se o foco se perdesse no EditText - então o usuário definitivamente terminou, no entanto, por experiência, parece que nem todos os widgets do Android estão captando o foco corretamente (tive esses problemas com Spinners e Botões) - para que outros widgets não percam o foco ...
AgentKnopf
3
por que uma plataforma tão grande nos permite lidar com esse tipo de funções simples? O Google não perderá nada se adicionar esses recursos principais a suas bibliotecas de suporte, pelo menos
MBH
Se você souber o comprimento exato da string que o usuário irá inserir, obtenha o texto em afterTextChanged. Ex:override fun afterTextChanged(s: Editable?) { if (s.toString().length == 5) { val enteredString = s.toString() }
Shylendra Madda
2
Estou coçando minha cabeça e me perguntando por que isso é tão difícil no Android que nós (eu inclusive) precisamos pesquisar e iniciar uma discussão online e muitas soluções diferentes com mais de 10 linhas de código ..?
nsandersen 01 de

Respostas:

119

Quando o usuário terminar a edição, ele / ela irá pressionar DoneouEnter

((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. 
        }
    }
);
Reno
fonte
50
Você pode ter certeza sobre isso? Tenho o hábito de acessar / acessar o próximo campo editável - praticamente nunca pressiono Enter / Done - e o que vi de nossos clientes, eles também não ... Estou falando sobre uma lista de Edittexts / Comboboxes etc. . Se você fornecer ao usuário apenas um campo de entrada e forçá-lo a pressionar um botão antes que ele possa avançar para outros campos, então isso é obviamente uma história diferente ...
AgentKnopf
5
Lembre-se, você também precisa ter android: singleLine = "true" definido para o texto de edição. Agradecimentos a stackoverflow.com/questions/15901863/…
steven smith
1
Boa solução, mas pode travar com um ponteiro nulo ... 'evento' pode ser nulo na ação Concluída, então event.isShiftPressed () falha. Se você usar isso, adicione verificações de ponteiro nulo.
Georgie
Editar adicionado para verificar se há "Evento" nulo.
Word Rearranger
3
e se você apertar o botão BACK que fecha o teclado?
user347187
183

Melhor forma, você também pode usar o ouvinte EditText onFocusChange para verificar se o usuário fez a edição: (não é necessário que o usuário pressione o botão Concluído ou Enter no teclado virtual)

 ((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) { {
         // Validate youredittext
      }
    }
 });

Nota: Para mais de um EditText, você também pode deixar sua classe implementar, em View.OnFocusChangeListenerseguida, definir os ouvintes para cada um de vocês EditText e validá-los conforme abaixo

((EditText)findViewById(R.id.edittext1)).setOnFocusChangeListener(this);
((EditText)findViewById(R.id.edittext2)).setOnFocusChangeListener(this);

    @Override
    public void onFocusChange(View v, boolean hasFocus) {

      // When focus is lost check that the text field has valid values.

      if (!hasFocus) {
        switch (view.getId()) {
           case R.id.edittext1:
                 // Validate EditText1
                 break;
           case R.id.edittext2:
                 // Validate EditText2
                 break;
        }
      }
    }
Vinayak Bevinakatti
fonte
60
Isso não funciona se houver apenas um focalizável EditText. Nunca perderá o foco.
Bart Friederichs
Qual é a melhor forma se tivermos apenas um EditText? Eu tenho que usar onEditorAction?
Tux
3
Se houver apenas um EditText, você valida tudo o que o usuário fizer para sair da tela.
Christine de
Eu tentei isso, mas não funcionou. Cada vez que pressionava enter, W/ViewRootImpl: Cancelling event due to no window focus: MotionEvent { action=ACTION_CANCEL, actionButton=0, id[0]=0, x[0]=605.52246, y[0]=969.4336, toolType[0]=TOOL_TYPE_FINGER, buttonState=0, metaState=0, flags=0x0, edgeFlags=0x0, pointerCount=1, historySize=0, eventTime=238238, downTime=235422, deviceId=0, source=0x1002 }recebia uma mensagem semelhante a esta: Mesmo que eu tenha digitado algo. Este é um texto de edição que aceita apenas números.
ahitt6345
1
Existe um ponto lateral de usar o ouvinte OnFocusChange: ele não será chamado se EditText tiver foco e o dispositivo for girado! Eu resolvi isso colocando someOtherView.requestFocus () em Activities onPause (). (antes de super.onPause ()) Dessa forma, é seguro que EditText perca o foco e o ouvinte seja chamado.
allofmex
60

Eu pessoalmente prefiro o envio automático após o fim da digitação. Veja como você pode detectar este evento.

Declarações e inicialização:

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

Ouvinte em, por exemplo, 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);
            }
        }
    });

Portanto, quando o texto é alterado, o cronômetro começa a aguardar as próximas alterações. Quando eles ocorrem, o temporizador é cancelado e reiniciado.

ZimaXXX
fonte
11
isto é incrível, mas cuidado que os temporizadores podem causar vazamentos de memória assim que usar Handlerem vez
Moh Mah
É ótimo, Thansk.
VipPunkJoshers Droopy
o que é serviceConnector?
altruísta de
@altruistic É apenas um fragmento do meu código do qual extruí a resposta. É o lugar onde a lógica deve ir.
ZimaXXX
21

Você pode fazer isso usando setOnKeyListener ou usando um textWatcher como:

Definir observador de texto editText.addTextChangedListener(textWatcher);

então ligue

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) {

        }
    };
ShineDown
fonte
27
Receio que esta não seja a solução (completa) - o textWatcher é acionado após cada edição - se você digitar olá, ele será acionado para h, e, l, le o - ele realmente não sabe quando o usuário está "pronto" . Seria ótimo se o TextWatcher soubesse realmente quando o widget perdeu o foco e o usuário mudou ...
AgentKnopf
4

Embora muitas respostas apontem na direção certa, acho que nenhuma delas responde o que o autor da pergunta estava pensando. Ou pelo menos entendi a pergunta de forma diferente porque estava procurando uma resposta para um problema semelhante. O problema é "Como saber quando o usuário para de digitar sem que ele pressione um botão" e acionar alguma ação (por exemplo, completar automaticamente). Se você quiser fazer isso, inicie o cronômetro em onTextChanged com um atraso que você consideraria que o usuário parou de digitar (por exemplo, 500-700ms), para cada nova letra ao iniciar o cronômetro cancele a anterior (ou pelo menos use algum tipo de sinalizar que, quando marcam, não fazem nada). Aqui está um código semelhante ao que usei:

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

Observe que eu modifico o sinalizador booleano em execução dentro da minha tarefa assíncrona (a tarefa obtém o json do servidor para preenchimento automático).

Também tenha em mente que isso cria muitas tarefas temporizadas (acho que elas estão agendadas no mesmo Tópico, mas você teria que verificar isso), então provavelmente há muitos lugares para melhorar, mas essa abordagem também funciona e o resultado final é que você deve use um cronômetro, pois não há nenhum "evento de usuário parou de digitar"

Igor Čordaš
fonte
Você pode fornecer mais informações e um exemplo de código sobre isso?
John Ernest Guadalupe
A essência do código está na resposta, para um exemplo mais detalhado, consulte a resposta de ZimaXXX nesta mesma página que usa a mesma abordagem. Não sei o que mais informações eu poderia adicionar, se você pode especificar o que não está claro, tentarei adicionar detalhes.
Igor Čordaš
4

@Reno e @Vinayak B respondem juntos se você quiser ocultar o teclado após a ação

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
        }
    }
});
Tyler Davis
fonte
3

Uma abordagem diferente ... aqui está um exemplo: Se o usuário tem um atraso de 600-1000ms quando está digitando, você pode considerar que ele está parado.

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

Cristi Maris
fonte
2

Ok, isso vai funcionar 100% com certeza.

Primeiro, você precisará configurar o ouvinte se o teclado for mostrar ou ocultar. Se o teclado estiver aparecendo, provavelmente o usuário está digitando, caso contrário, termine de digitar.

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;
                        }
                    }
            }
        });
Krit
fonte
1
que tal várias edições de texto? E o heightDiff > 100material é assustador imho.
Bondax
Não tem problema, funciona bem. Dê uma olhada neste stackoverflow.com/questions/2150078/…
Krit
2

Eu resolvi esse problema dessa forma. Eu usei o kotlin.

        var timer = Timer()
        var DELAY:Long = 2000

        editText.addTextChangedListener(object : TextWatcher {

            override fun afterTextChanged(s: Editable?) {
                Log.e("TAG","timer start")
                timer = Timer()
                timer.schedule(object : TimerTask() {
                    override fun run() {
                        //do something
                    }
                }, DELAY)
            }

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

            override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
                Log.e("TAG","timer cancel ")
                timer.cancel() //Terminates this timer,discarding any currently scheduled tasks.
                timer.purge() //Removes all cancelled tasks from this timer's task queue.
            }
        })
Fahed Hermoza
fonte
0

Eu tive o mesmo problema e não queria contar com o usuário pressionando Concluído ou Enter.

Minha primeira tentativa foi usar o ouvinte onFocusChange, mas ocorreu que meu EditText obteve o foco por padrão. Quando o usuário pressionou alguma outra visualização, o onFocusChange foi acionado sem que o usuário nunca tivesse atribuído o foco.

A próxima solução fez isso para mim, em que onFocusChange é anexado se o usuário tocou em 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
                    }
                }
            }
        }
}

No meu caso, quando o usuário terminou a edição, a tela foi renderizada novamente, renovando assim o objeto myEditText. Se o mesmo objeto for mantido, você provavelmente deve remover o ouvinte onFocusChange em onFocusChange para evitar o problema de onFocusChange descrito no início desta postagem.

Jos
fonte
Você está configurando um ouvinte de mudança de foco sempre que ocorre um evento de toque, o que acontecerá várias vezes por segundo. Não faça isso.
jsonfry
0

Eu tive o mesmo problema ao tentar implementar 'agora digitando' no aplicativo de bate-papo. tente estender EditText da seguinte maneira:

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

}

Nadav
fonte
0

Eu terminei ela com o mesmo problema e não consegui usar a solução com onEditorAction ou onFocusChange e não queria tentar o cronômetro. Um cronômetro é muito perigoso para gosto, por causa de todos os threads e muito imprevisível, já que você não sabe quando seu código é executado.

O onEditorAction não detecta quando o usuário sai sem usar um botão e, se você usá-lo, observe que KeyEvent pode ser nulo. O foco não é confiável em ambas as extremidades, o usuário pode obter o foco e sair sem inserir nenhum texto ou selecionar o campo e o usuário não precisa sair do último campo EditText.

Minha solução usa onFocusChange e um sinalizador definido quando o usuário começa a editar o texto e uma função para obter o texto da última visualização em foco, que chamo quando necessário.

Eu apenas limpo o foco em todos os meus campos de texto para enganar o código de exibição de texto de saída. O código clearFocus só é executado se o campo tiver foco. Eu chamo a função em onSaveInstanceState para que não precise salvar o sinalizador (mEditing) como um estado da visualização EditText e quando botões importantes são clicados e quando a atividade é fechada.

Tenha cuidado com o TexWatcher, pois ele é chamado frequentemente. Eu uso a condição em foco para não reagir quando o código onRestoreInstanceState insere texto. Eu

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

Eu fiz algo como esta classe abstrata que pode ser usada no lugar do tipo 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
}
Michał Ziobro
fonte
0

Simples de acionar, terminar de digitar em EditText

funcionou para mim, se você estiver usando java, convertê-lo

Em Kotlin

youredittext.doAfterTextChanged { searchTerm ->
val currentTextLength = searchTerm?.length
    Handler().postDelayed({
        if (currentTextLength == searchTerm?.length) {
            // your code 
           Log.d("aftertextchange", "ON FINISH TRIGGER")
          }
       }, 3000)
}
Kharis Azhar
fonte