Metin İzleyiciyi tetiklemeden Metni Düzenle metnini nasıl değiştirebilirim?


104

Bir var EditTextüzerinde bir müşteri Metin İzleyici ile alan. Bir kod parçasında, kullandığım EditText'teki değeri değiştirmem gerekiyor .setText("whatever").

Sorun şu ki, bu değişikliği yapar afterTextChangedyapmaz yöntem çağrılıyor ve bu da sonsuz bir döngü yaratıyor. TextChanged sonrasında tetiklemeden metni nasıl değiştirebilirim?

AfterTextChanged yöntemindeki metne ihtiyacım var, bu yüzden TextWatcher.

Yanıtlar:


70

İzleyicinin kaydını silip yeniden kaydettirebilirsiniz.

Alternatif olarak, izleyicinizin metni ne zaman değiştirdiğinizi bilmesi için bir bayrak koyabilirsiniz (ve bu nedenle onu görmezden gelmelisiniz).


İpucun benim için yeterli. Aslında dinleyiciyi onResume'a kaydediyordum ama onPause () 'da kaydını silmiyordum, bu yüzden birden çok kez arıyordu.
Smeet

Birisi bunu açıklayabilir mi? Teknik olarak, android'de yeniyim, daha fazla ayrıntıya ihtiyacım var, teşekkürler.
Budi Mulyo

116

Kısa cevap

Kullanıcı ve program tarafından tetiklenen olayları ayırt etmek için şu anda hangi Görünümün odaklandığını kontrol edebilirsiniz.

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

myEditText.addTextChangedListener(new TextWatcher() {

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

    //...
});

Uzun cevap

Kısa yanıta ek olarak: myEditTextAramanız gereken metni programlı olarak değiştirdiğinizde odak zaten varsa clearFocus(), sonra setText(...)ararsınız ve odağı yeniden talep ettikten sonra. Bunu bir yardımcı program işlevine koymak iyi bir fikir olacaktır:

void updateText(EditText editText, String text) {
    boolean focussed = editText.hasFocus();
    if (focussed) {
        editText.clearFocus();
    }
    editText.setText(text);
    if (focussed) {
        editText.requestFocus();
    }
}

Kotlin için:

Kotlin, uzantı işlevlerini desteklediğinden, yardımcı program işleviniz şöyle görünebilir:

fun EditText.updateText(text: String) {
    val focussed = hasFocus()
    if (focussed) {
        clearFocus()
    }
    setText(text)
    if (focussed) {
        requestFocus()
    }
}

fragment getActivity().getCurrentFocus()and kotlinactivity?.currentFocus
Mohammad Reza Khahani

@MohammadRezaKhahani Hey! buna ihtiyacın yok. hasFocus()Doğrudan üzerinden arayabilirsiniz EditText.
Willi Mentzel

Uygun değil. Edittext odaklanmış olsa bile tetik dinleyici olmadan setText () 'i ayarlamak istersem ne olur?
Jaya Prakash

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

Kullanım:

et_text.addTextChangedListener(new MyTextWatcher(et_text));

Eğer kullanıyorsanız hızla metin girerken Sen biraz gecikme hissedebilirsiniz editText.setText () yerine editable.replace () .


Bu neden reddedildi? Bu benim sorunum için mükemmel bir çözüm iken diğerleri işe yaramadı. Ancak bu çözümün çalışması için TextWatcher'ı alt sınıflara ayırmanın gerekli olmadığını da ekleyeceğim. Chan Chun O'na teşekkürler.
Michael Fulton

TextWatcher'ın kaydını silme ve yeniden kaydetme fikriniz harika çalışıyor. Ancak, et.setText ("") çalışırken, s.replace (0, s.length (), "") çalışmaz.
stevehs17

@ stevehs17, kodunuzun nasıl olduğunu gerçekten bilmiyorum ama kullanım çok detaylı güncellendi, bir deneyin.
Chan Chun Him

15

Düzeltmesi kolay bir numara ... Yeni düzenleme metni değerini türetme mantığınız idempotent olduğu sürece (muhtemelen bu olurdu, ama sadece söylüyorum). Dinleyici yönteminizde, düzenleme metnini yalnızca mevcut değer, değeri en son değiştirdiğiniz zamandan farklıysa değiştirin.

Örneğin,

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

1
Bu yine de yöntemin iki kez çağrılmasına neden olur, değil mi?
Trevor Hart

Evet, bu yüzden idempotent olması gerektiğini belirttim (bunu daha açık hale getirmeliydim) ... ideal değil ama işe yaramayan tek bir yöntem çağrısının ek yükü konusunda endişeliyseniz, gerçekten sert optimizasyon yapmalısınız. Öyleyse, dinleyiciyi aramadan önce kaldırmak ve setText()daha sonra yeniden eklemek için kodu yeniden düzenleyebilirsiniz .
Jeffrey Blattman

6

Ben şu şekilde kullanıyorum:

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

Metni programatik olarak değiştirmeniz gerektiğinde, önce odağı temizleyin

mEditText.clearFocus();
mEditText.setText(lastAddress.complement);

5

Bunun için genel çözüme sahip olmak için Kotlin DSL sözdizimini kullanabilirsiniz:

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

TextWatcher'ınızın içinde şu şekilde kullanabilirsiniz:

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

Çok daha temiz. Kotlin DSL harika
Anjal Saneen

4

Bu benim için iyi çalışıyor

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

3

Sorun, tagdosyalanmış kullanılarak kolayca çözülebilir ve editText'in odak noktasıyla uğraşmanıza bile gerek kalmaz.

Metni ve etiketi programlı olarak ayarlama

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

tagOnTextChanged için kontrol ediliyor

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

3

EditTextMetni değiştirmeye odaklanmanız gerekiyorsa, odaklanmayı isteyebilirsiniz:

if (getCurrentFocus() == editText) {
    editText.clearFocus();
    editText.setText("...");
    editText.requestFocus();
}

1

şu mantığı deneyin: sonsuz döngüye girmeden setText ("") 'i ayarlamak istedim ve bu kod benim için çalışıyor. Umarım bunu ihtiyacınıza uyacak şekilde değiştirebilirsiniz

        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

Değişiklikleri meydana geldikçe görmek istemenin normal durumu için TextWatcher'dan daha basit bir arayüz sağlayan kullanışlı bir sınıf. Ayrıca, OP'nin talep ettiği bir sonraki değişikliğin göz ardı edilmesine izin verir.

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

Bunu şu şekilde kullanın:

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

editTextYinelemeli düzenlemelere neden olmadan içeriğini değiştirmek istediğinizde , şunu yapın:

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

0

Benim varyantım:

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


}

Dinleyicileri yalnızca setOnTextChangeListener () kullanarak ayarlayın ve metni yalnızca setNewText kullanarak ayarlayın (setText () işlevini geçersiz kılmak istedim, ancak sonuncu)


0

Bir TextWatcher aracılığıyla EditText'te bir değişiklik yapıldığında oluşan döngüsel sorunu azaltan soyut bir sınıf oluşturdum.

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

Çok basit, bu yöntemle metni ayarlayın

void updateText(EditText et, String text) {
   if (!et.getText().toString().equals(text))
       et.setText(text);
}

-2

Metin değişiklikleri uygulamanızın istikrarlı olmasını ve herhangi bir değişiklik gerekmiyorsa metni değiştirmemesini sağlamalısınız. Normalde bu, izleyiciden bir kez geçmiş olan herhangi bir içerik olurdu.

En yaygın hata, metin gerçekten değişmemiş olsa bile, ilişkili EditText veya Editable'da yeni bir metin ayarlamaktır.

Bunun da ötesinde, değişikliklerinizi belirli bir Görünüm yerine Düzenlenebilir'de yaparsanız, izleyicinizi kolayca yeniden kullanabilir ve ayrıca istediğiniz sonuca sahip olduğundan emin olmak için bazı birim testleriyle tek başına test edebilirsiniz.

Düzenlenebilir bir arabirim olduğundan, kararlı olması gereken içeriği test ederken, içeriğini değiştirmeye çalışan yöntemlerinden herhangi biri çağrılırsa bir RuntimeException oluşturan sahte bir uygulaması bile kullanabilirsiniz.


-2

Bunu yapmanın yolu:

Yazma bölümünde

        EditText e_q;

        e_q = (EditText) parentView.findViewWithTag("Bla" + i);

        int id=e_q.getId();
        e_q.setId(-1);
        e_q.setText("abcd...");
        e_q.setId(id);

Dinleyici

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

        int id = view.getId();
        if(id==-1)return;

        ....

Yine de çalışıyor.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.