android no Text Change Listener

265

Eu tenho uma situação, onde existem dois campos. field1e field2. Tudo o que quero fazer é vazio field2quandofield1 é alterado e vice-versa. Portanto, no final, apenas um campo tem conteúdo.

field1 = (EditText)findViewById(R.id.field1);
field2 = (EditText)findViewById(R.id.field2);

field1.addTextChangedListener(new TextWatcher() {

   public void afterTextChanged(Editable s) {}

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

   public void onTextChanged(CharSequence s, int start,
     int before, int count) {
      field2.setText("");
   }
  });

field2.addTextChangedListener(new TextWatcher() {

   public void afterTextChanged(Editable s) {}

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

   public void onTextChanged(CharSequence s, int start,
     int before, int count) {
     field1.setText("");
   }
  });

Ele funciona muito bem se eu anexar addTextChangedListenera field1única, mas quando eu faço isso para ambos os campos do aplicativo falha. Obviamente, porque eles tentam mudar um ao outro indefinidamente. Depois que as field1alterações forem apagadas field2, neste momento, as field2alterações serão alteradas, para quefield1 e assim por diante ...

Alguém pode sugerir alguma solução?

inrob
fonte
para novos usuários, escolha uma ligação de dados bidirecional usando um campo observável de sequência de caracteres, pois toda a solução fornecida aqui poderá produzir starting waiting blocking gc allocesse tipo de erro, que pode até causar travamentos e travamentos. seguro e recomendado pelo google agora ..
Maifee Ul Asad

Respostas:

460

Você pode adicionar uma verificação para limpar somente quando o texto no campo não estiver vazio (ou seja, quando o comprimento for diferente de 0).

field1.addTextChangedListener(new TextWatcher() {

   @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(s.length() != 0)
        field2.setText("");
   }
  });

field2.addTextChangedListener(new TextWatcher() {

   @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(s.length() != 0)
         field1.setText("");
   }
  });

Documentação para TextWatcher aqui .

Também respeite as convenções de nomenclatura .

user2336315
fonte
1
como detectar depois que todo o campo foi alterado, porque ele é detectado toda vez que é alterado, quando qualquer botão é pressionado.
Rafael Guimarães
20

Eu sei que isso é antigo, mas alguém pode se deparar com isso novamente algum dia.

Eu tive um problema semelhante em que chamaria setText em um EditText e onTextChanged seria chamado quando não quisesse. Minha primeira solução foi escrever um código depois de chamar setText () para desfazer o dano causado pelo ouvinte. Mas isso não era muito elegante. Depois de fazer algumas pesquisas e testes, descobri que o uso de getText (). Clear () limpa o texto da mesma maneira que setText (""), mas como não está definindo o texto, o ouvinte não é chamado, de modo que resolveu meu problema. Troquei todas as minhas chamadas setText ("") para getText (). Clear () e não precisei mais das ataduras, portanto, talvez isso também resolva o seu problema.

Tente o seguinte:

Field1 = (EditText)findViewById(R.id.field1);
Field2 = (EditText)findViewById(R.id.field2);

Field1.addTextChangedListener(new TextWatcher() {

   public void afterTextChanged(Editable s) {}

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

   public void onTextChanged(CharSequence s, int start,
     int before, int count) {
      Field2.getText().clear();
   }
  });

Field2.addTextChangedListener(new TextWatcher() {

   public void afterTextChanged(Editable s) {}

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

   public void onTextChanged(CharSequence s, int start,
     int before, int count) {
     Field1.getText().clear();
   }
  });
RTHarston
fonte
11

Se você estiver usando o Kotlin para desenvolvimento Android, poderá adicionar TextChangedListener()usando este código:

myTextField.addTextChangedListener(object : TextWatcher{
        override fun afterTextChanged(s: Editable?) {}

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

        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
    })
iHulk
fonte
5

Um pouco atrasado de uma resposta, mas aqui está uma solução reutilizável:

/**
 * 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;
        }
    }

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

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

    protected abstract void afterTextChange(Editable s);
}

Portanto, quando o descrito acima é usado, todas as setText()chamadas realizadas no TextWatcher não resultam na chamada do TextWatcher novamente:

/**
 * A setText() call in any of the callbacks below will not result in TextWatcher being 
 * called again.
 */
public class MyTextWatcher extends EditableTextWatcher {

    @Override
    protected void beforeTextChange(CharSequence s, int start, int count, int after) {
    }

    @Override
    protected void onTextChange(CharSequence s, int start, int before, int count) {
    }

    @Override
    protected void afterTextChange(Editable s) {
    }
}
Eurig Jones
fonte
5

Também enfrentei o mesmo problema e continuo obtendo stackOverflowexceções e venho com a seguinte solução.

edt_amnt_sent.addTextChangedListener(new TextWatcher() {    
    @Override
    public void afterTextChanged(Editable s) {
        if (skipOnChange)
            return;

        skipOnChange = true;
        try {
            //method
        } catch (NumberFormatException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            skipOnChange = false;
        }
    }
});

edt_amnt_receive.addTextChangedListener(new TextWatcher() {

    @Override
    public void afterTextChanged(Editable s) {

        if (skipOnChange)
            return;

        skipOnChange = true;
        try {
            //method
        } catch (NumberFormatException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            skipOnChange = false;
        }
    }
});

declarado inicialmente bipeano skipOnChange = false;

Sumit
fonte
1
"stack full" Eu acho que você quer dizer Stack Overflow;)
A Droid
4

Você também pode usar o método hasFocus ():

public void onTextChanged(CharSequence s, int start,
     int before, int count) {
     if (Field2.hasfocus()){
         Field1.setText("");
     }
   }

Testei isso para um trabalho na faculdade em que eu estava trabalhando para converter escalas de temperatura conforme o usuário as digitava. Funcionou perfeitamente e é muito mais simples.

renomear
fonte
1
E o editText.setText quando o usuário insere? EditText tem foco neste caso
Evgenii Vorobei
melhor solução .
Syed Hissaan
3

marque String antes de definir outro EditTextcomo vazio. se Field1estiver vazio, por que precisar mudar novamente para ("")? para que você possa verificar o tamanho da sua string com s.lenght () ou qualquer outra solução

Outra maneira de verificar o comprimento da String é:

String sUsername = Field1.getText().toString();
if (!sUsername.matches(""))
{
// do your job
}
Shayan Pourvatan
fonte
2

Eu escrevi minha própria extensão para isso, muito útil para mim. (Kotlin)

Você pode escrever apenas assim:

editText.customAfterTextChanged { editable -> 
    //You have accessed the editable object. 
}

Minha extensão:

fun EditText.customAfterTextChanged(action: (Editable?)-> Unit){
    this.addTextChangedListener(object : TextWatcher {
       override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
       override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {}
       override fun afterTextChanged(editable: Editable?) {
        action(editable)
    }
})}
Burak Dizlek
fonte
2
editText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }
            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                if (noteid != -1) {
                    MainActivity.notes.set(noteid, String.valueOf(charSequence));
                    MainActivity.arrayAdapter.notifyDataSetChanged();
                }
            }
            @Override
            public void afterTextChanged(Editable editable) {

            }
        });

neste código, noteid é basicamente argumentos retirados, que estão sendo colocados no recuo ou passados ​​pelo recuo.

  Intent intent = getIntent();
         noteid = intent.getIntExtra("noteid", -1);

o código do lado negativo é basicamente o código extra, se você quiser entender mais claramente.

how to make the menu or insert the menu in our code , 
    create the  menu folder this the folder created by going into the raw
    ->rightclick->
    directory->name the folder as you wish->
    then click on the directory formed->
    then click on new file and then name for file as you wish ie the folder name file
    and now type the 2 lines code in it and see the magic.

Com o novo código de atividade chamado NoteEditor.java para fins de edição, meu aplicativo é basicamente o aplicativo de anotações.

package com.example.elavi.notes;
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;
import android.widget.Toast;

import static android.media.CamcorderProfile.get;
public class NoteEditorActivity extends AppCompatActivity {
    EditText editText;
    int noteid;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_note_editor);
        editText = findViewById(R.id.editText);
        Intent intent = getIntent();
         noteid = intent.getIntExtra("noteid", -1);
        if (noteid != -1) {
            String text = MainActivity.notes.get(noteid);
            editText.setText(text);

           Toast.makeText(getApplicationContext(),"The arraylist content is"+MainActivity.notes.get(noteid),Toast.LENGTH_SHORT).show();
        }
        else
        {
            Toast.makeText(getApplicationContext(),"Here we go",Toast.LENGTH_SHORT).show();
            MainActivity.notes.add("");
            noteid=MainActivity.notes.size()-1;
        }
        editText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {

            }
            @Override
            public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
                if (noteid != -1) {
                    MainActivity.notes.set(noteid, String.valueOf(charSequence));
                    MainActivity.arrayAdapter.notifyDataSetChanged();
                }
            }
            @Override
            public void afterTextChanged(Editable editable) {

            }
        });
    }
}
Lavish Garg
fonte
1

No Kotlin, basta usar a função de extensão KTX : (Utiliza TextWatcher)

yourEditText.doOnTextChanged { text, start, count, after -> 
        // action which will be invoked when the text is changing
    }


importação core-KTX:

implementation "androidx.core:core-ktx:1.2.0"
Francis
fonte
1

Podemos remover o TextWatcher de um campo antes de editar seu texto e adicioná-lo novamente após editar o texto.

Declare os Observadores de Texto para o campo1 e o campo2 como variáveis ​​separadas para dar um nome a eles: por exemplo, para o campo1

private TextWatcher Field_1_Watcher = new TextWatcher() {
    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    }

    @Override
    public void afterTextChanged(Editable s) {
    }

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

    }

};

adicione o observador usando seu nome: field1.addTextChangedListener(Field_1_Watcher)para o campo1 e field2.addTextChangedListener(Field_2_Watcher)para o campo2

Antes de alterar o texto do campo2 , remova o TextWatcher: field2.removeTextChangedListener(Field_2_Watcher) altere o texto: field2.setText("")

adicione o TextWatcher novamente: field2.addTextChangedListener(Field_2_Watcher)

Faça o mesmo para o outro campo

Keith Kiarie
fonte
-3

Adicione plano de fundo dinamicamente no onCreatemétodo:

getWindow().setBackgroundDrawableResource(R.drawable.background);

Também remova o plano de fundo do XML.

Ankit Kumar
fonte