Как удалить всех слушателей, добавленных с помощью addTextChangedListener
У меня есть ListView
где каждый ряд имеет EditText
контроль. Я хочу добавить TextChangedListener
к каждому ряду; тот, который содержит дополнительные данные, которые говорят, какая строка EditText
был в. Проблема в том, что как getView
вызывается, несколько TextWatchers
добавлены; поскольку convertView
уже имея TextWatcher
(и тот, который указывает на другую строку).
MyTextWatcher watcher = new MyTextWatcher(currentQuestion);
EditText text = (EditText)convertView.findViewById(R.id.responseText);
text.addTextChangedListener(watcher);
MyTextWatcher
мой класс, который реализует TextWatcher
; и обрабатывает текстовые события. CurrentQuestion позволяет мне узнать, по какой строке я действую. Когда я печатаю в поле; несколько случаев TextWatcher
называются.
Есть ли способ удалить TextWatchers
перед добавлением нового? Я вижу removeTextChangedListener
метод, но это требует определенного TextWatcher
чтобы передать, и я не знаю, как получить указатель на TextWatcher
это уже там.
15 ответов
Там нет никакого способа сделать это с помощью текущего EditText
интерфейс напрямую. Я вижу два возможных решения:
- Редизайн приложения, чтобы вы всегда знали, что
TextWatcher
добавляются в частностиEditText
пример. - простираться
EditText
и добавить возможность очистить всех наблюдателей.
Вот пример второго подхода - ExtendedEditText
:
public class ExtendedEditText extends EditText
{
private ArrayList<TextWatcher> mListeners = null;
public ExtendedEditText(Context ctx)
{
super(ctx);
}
public ExtendedEditText(Context ctx, AttributeSet attrs)
{
super(ctx, attrs);
}
public ExtendedEditText(Context ctx, AttributeSet attrs, int defStyle)
{
super(ctx, attrs, defStyle);
}
@Override
public void addTextChangedListener(TextWatcher watcher)
{
if (mListeners == null)
{
mListeners = new ArrayList<TextWatcher>();
}
mListeners.add(watcher);
super.addTextChangedListener(watcher);
}
@Override
public void removeTextChangedListener(TextWatcher watcher)
{
if (mListeners != null)
{
int i = mListeners.indexOf(watcher);
if (i >= 0)
{
mListeners.remove(i);
}
}
super.removeTextChangedListener(watcher);
}
public void clearTextChangedListeners()
{
if(mListeners != null)
{
for(TextWatcher watcher : mListeners)
{
super.removeTextChangedListener(watcher);
}
mListeners.clear();
mListeners = null;
}
}
}
А вот как вы можете использовать ExtendedEditText
в макетах XML:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ua.inazaruk.HelloWorld.ExtendedEditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="header"
android:gravity="center" />
</LinearLayout>
Вы можете удалить TextWatcher из вашего EditText. Прежде всего, я предлагаю вам переместить объявление TextWatcher за пределы editText.addTextChangedListener(...):
protected TextWatcher yourTextWatcher = new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
// your logic here
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// your logic here
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// your logic here
}
};
После этого вы сможете установить TextWather немного проще:
editText.addTextChangedListener(yourTextWatcher);
Чем вы можете удалить TextWatcher следующим образом:
editText.removeTextChangedListener(yourTextWatcher);
и установите другой, если хотите.
Я также потратил много времени на поиск решения и, наконец, решил с помощью тега, как показано ниже. Он удалит предыдущие экземпляры TextWatcher, получив ссылки из тега convertView. Это прекрасно решает проблему. В вашем файле CustomAdapter установите новый внутренний класс, как показано ниже:
private static class ViewHolder {
private TextChangedListener textChangedListener;
private EditText productQuantity;
public EditText getProductQuantity() {
return productQuantity;
}
public TextChangedListener getTextChangedListener() {
return textChangedListener;
}
public void setTextChangedListener(TextChangedListener textChangedListener) {
this.textChangedListener = textChangedListener;
}
}
Затем в вашем переопределенном общедоступном методе View getView (int position, View convertView, ViewGroup parent) реализуйте логику, подобную приведенной ниже:
@Override
public View getView(int position, View convertView, ViewGroup parent) {
EditText productQuantity;
TextChangedListener textChangedListener;
if(convertView==null) {
LayoutInflater mInflater = (LayoutInflater)
context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
convertView = mInflater.inflate(R.layout.cart_offer_item, parent, false);
productQuantity=(EditText)convertView.findViewById(R.id.productQuantity);
addTextChangedListener(viewHolder, position);
convertView.setTag(viewHolder);
}
else
{
ViewHolder viewHolder=(ViewHolder)convertView.getTag();
productQuantity=viewHolder.getProductQuantity();
removeTextChangedListener(viewHolder);
addTextChangedListener(viewHolder, position);
}
return convertView;
}
private void removeTextChangedListener(ViewHolder viewHolder)
{
TextChangedListener textChangedListener=viewHolder.getTextChangedListener();
EditText productQuantity=viewHolder.getProductQuantity();
productQuantity.removeTextChangedListener(textChangedListener);
}
private void addTextChangedListener(ViewHolder viewHolder, int position)
{
TextChangedListener textChangedListener=new TextChangedListener(position);
EditText productQuantity=viewHolder.getProductQuantity();
productQuantity.addTextChangedListener(textChangedListener);
viewHolder.setTextChangedListener(textChangedListener);
}
Затем реализуйте класс TextWatcher, как показано ниже:
private class TextChangedListener implements TextWatcher
{
private int position;
TextChangedListener(int position)
{
this.position=position;
}
@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) {
Log.d("check", "text changed in EditText");
}
}
Он удалит предыдущие экземпляры TextWatcher, получив ссылки из тега convertView
Давно этот вопрос не задавался, но может кому пригодится. Проблема с TextWatcher в Recyclerview заключается в том, что мы должны убедиться, что он удален до того, как представление будет переработано. В противном случае мы потеряем экземпляр TextWatcher, а вызов removeTextChangedListener(textWatcher) в OnBindViewHolder() удалит только текущий экземпляр TextWatcher.
Я решаю эту проблему, добавляя TextChangedListener внутрь FocusChangedListener:
editText.setOnFocusChangeListener(new OnFocusChangeListener() {
public void onFocusChange(View v, boolean hasFocus) {
if(hasFocus) {
editText.addTextChangedListener(textWatcher)
}
else{
editText.removeTextChangedListener(textWatcher)
}
}
});
Таким образом, я уверен, что когда editText не имеет фокуса, текстовый наблюдатель удаляется и снова добавляется, когда он имеет фокус. Таким образом, когда recyclerview перерабатывается, в editText будет удален любой textChangeListener.
Сохраните текущий текстовый наблюдатель в окне просмотра, и вы сможете найти тот, который хотите удалить.
Я боролся с аналогичной проблемой с большим количеством EditTexts в RecyclerView. Я решил это размышлением. Вызов
ReflectionTextWatcher.removeAll(your_edittext)
перед связыванием просмотров. Этот фрагмент кода находит все TextWatcher и удаляет их из локального списка EditText, который называется "mListeners".
public class ReflectionTextWatcher {
public static void removeAll(EditText editText) {
try {
Field field = findField("mListeners", editText.getClass());
if (field != null) {
field.setAccessible(true);
ArrayList<TextWatcher> list = (ArrayList<TextWatcher>) field.get(editText); //IllegalAccessException
if (list != null) {
list.clear();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private static Field findField(String name, Class<?> type) {
for (Field declaredField : type.getDeclaredFields()) {
if (declaredField.getName().equals(name)) {
return declaredField;
}
}
if (type.getSuperclass() != null) {
return findField(name, type.getSuperclass());
}
return null;
}
}
Надеюсь, это кому-то поможет.
Если кто-то, как я, имеет дело с ViewHolder
, то простое сохранение ссылки на наблюдатель текста при его создании не поможет. При повторном использовании представление перейдет к другомуViewHolder
который не будет иметь ссылки на этот старый наблюдатель текста, поэтому его нельзя будет удалить.
Лично я решил решить такую проблему, как @inazaruk, хотя обновленный код на Kotlin + переименовал класс, чтобы лучше отразить его цель.
class EditTextWithRemovableTextWatchers(context: Context?, attrs: AttributeSet?) : TextInputEditText(context, attrs) {
private val listeners by lazy { mutableListOf<TextWatcher>() }
override fun addTextChangedListener(watcher: TextWatcher) {
listeners.add(watcher)
super.addTextChangedListener(watcher)
}
override fun removeTextChangedListener(watcher: TextWatcher) {
listeners.remove(watcher)
super.removeTextChangedListener(watcher)
}
fun clearTextChangedListeners() {
for (watcher in listeners) super.removeTextChangedListener(watcher)
listeners.clear()
}
}
Я разрешил эту ситуацию без расширения класса TextView.
private ArrayList<TextWatcher> mEditTextWatcherList = new ArrayList<>();
private TextWatcher mTextWatcher1;
private TextWathcer mTextWatcher2;
mTextWathcer1 = 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) {}
};
mTextWathcer2 = 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) {}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
setListener(mTextWatcher1);
setListener(mTextWatcher2);
removeListeners();
}
private setListener(TextWatcher listener) {
mEditText.addTextChangedListener(listener);
mEditTextWatcherList.add(listener);
}
private removeListeners() {
for (TextWatcher t : mEditTextWatcherList)
mEditText.removeTextChangedListener(t);
mEditTextWatcherList.clear();
}
Как вы можете видеть здесь: CodeSearch TextView нет способа удалить всех слушателей. Единственный способ - предоставить наблюдателя, который вы использовали для его регистрации.
Я еще не до конца понимаю, почему уже зарегистрированы другие слушатели. Однако вы можете создать подкласс EditText, переопределить addTextChangedListener (..) и сохранить в нем копию всех добавленных ссылок самостоятельно, а затем делегировать реализацию суперкласса. Затем вы можете также предоставить дополнительный метод, который удаляет всех слушателей.
Свяжитесь с нами, если вам нужны дальнейшие объяснения.
Я столкнулся с проблемой при использовании in inRecyclerView
элемент, и это вызывало ошибку бесконечного цикла, когдаViewHolder
была привязка, потому что был вызван добавленный в предыдущем вызове привязки, следовательно, бесконечный цикл..
Единственным рабочим решением для этого было хранить 's в списке, а затем вonBindViewHolder
, просмотрите этот список и удалите из .
private val textWatchers: MutableList<TextWatcher> = mutableListOf()
Добавить в список, прежде чем назначать его:
textWatchers.add(textWatcher1)
vh.moneyAmount.editText?.addTextChangedListener(textWatcher1)
Удалите их при привязке изделия, перебирая всюtextWatcherList
:
private fun removeTextWatcher(vh: MoneyItemViewHolder) {
textWatchers.forEach { vh.moneyAmount.editText?.removeTextChangedListener(it) }
}
Другого способа удалить 's изEditText
, чем прохождениеTextWatcher
объект, поэтому его нужно где-то хранить, если мы планируем удалить его позже.
Я боролся с подобной проблемой. Я решил это, сохранив ссылки на мои textWatchers в ArrayList
:
private final List<TextWatcher> textWatchersForProfileNameTextBox = new ArrayList<>();
public void addTextWatcherToProfileNameTextBox(TextWatcher textWatcher){
textWatchersForProfileNameTextBox.add(textWatcher);
getProfileNameTextView().addTextChangedListener(textWatcher);
}
public void removeAllTextWatchersFromProfileNameTextView(){
while (!textWatchersForProfileNameTextBox.isEmpty())
getProfileNameTextView().removeTextChangedListener(textWatchersForProfileNameTextBox.remove(0));
}
У меня была такая же проблема с xamarin/C#, и я написал для этого класс для управления событиями щелчка внутри ListView, где представление элемента будет "переработано":
public class ViewOnClickEventHandler: Java.Lang.Object
{
private List<EventHandler> EventList { get; set; }
public void SetOnClickEventHandler(View view, EventHandler eventHandler)
{
if (view.Tag != null)
{
ViewOnClickEventHandler holder = ((ViewOnClickEventHandler)view.Tag);
foreach (EventHandler evH in holder.EventList)
view.Click -= evH;
for (int i = 0; i < holder.EventList.Count; i++)
holder.EventList[i] = null;
holder.EventList.Clear();
}
EventList = new List<EventHandler>();
EventList.Add(eventHandler);
view.Click += eventHandler;
view.Tag = this;
}
}
Вы можете использовать его в вашем методе ListView BaseAdapter GetItem следующим образом:
TextView myTextView = convertView.FindViewById<TextView>(Resource.Id.myTextView);
ViewOnClickEventHandler onClick = new ViewOnClickEventHandler();
onClick.SetOnClickEventHandler(myTextView, new EventHandler(delegate (object sender, EventArgs e)
{
// Do whatever you want with the click event
}));
Класс ViewOnClickEventHandler будет заботиться о нескольких событиях в вашем текстовом представлении. Вы также можете изменить класс для событий textchange. Это тот же принцип. Я надеюсь, это поможет.
пока, nxexo007
То, что я сделал для удаления наблюдателей текста, очень просто. Я создал массив для размещения моих текстовых наблюдателей:
final TextWatcher[] textWatchers = new TextWatcher[3];
Я добавил их в:
final int CURRENT_PIN_CHECK = 0, NEW_PIN = 1, CONFIRM_PIN_CHECK = 2;
textWatchers[CURRENT_PIN_CHECK] = returnTextWatcherCheckPIN(CURRENT_PIN_CHECK);
textWatchers[NEW_PIN] = returnTextWatcherCheckPIN(NEW_PIN);
textWatchers[CONFIRM_PIN_CHECK] = returnTextWatcherCheckPIN(CONFIRM_PIN_CHECK);
Мой метод returnTextWatcherCheckPIN создает экземпляр textWatcher с другим средством проверки (switchMethod для проверки всех четырех editTexts) в afterTextChanged.
Затем, когда я удаляю средство просмотра текста, я просто ссылаюсь на него из массива:
etPin4.removeTextChangedListener(textWatchers[CURRENT_PIN_CHECK]);
Проверьте размер слушателей editText на отладке:
Это удалено! Это решило мою проблему!
Почему бы не прикрепить ссылку textwatcher к самому тексту редактирования с помощью setTag()
if(etTagValue.getTag(R.id.textWatcherTag)!=null){
etTagValue.removeTextChangedListener((TextWatcher) etTagValue.getTag());
}
etTagValue.setText(myValue);
TextWatcher textWatcher = new DelayedTextWatcher(text -> meta.setDescription(text.toString()));
etTagValue.addTextChangedListener(textWatcher);
etTagValue.setTag(R.id.textWatcherTag,textWatcher);
В ids.xml в разделе /values
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="textWatcherTag" type="id" />
</resources>
Поскольку я один из тех, кто сталкивается с множественными проблемами прослушивателя EditText doOnTextChanged в адаптере при использовании notifiDataSetChange. После стольких исследований и попыток множества способов я нашел решение здесь, в разделе ответов, предоставленном @Davi. И теперь я создал встроенную функцию Kotlin для более простого использования, в дополнение к ответу г-на @Davi. Надеюсь, это поможет вам, ребята
inline fun EditText.doAfterTextChangedMultipleListenerFix(
crossinline afterTextChanged: (text: Editable?) -> Unit
) {
val textWatcher = object : TextWatcher {
override fun afterTextChanged(s: Editable?) {
afterTextChanged.invoke(s)
}
override fun beforeTextChanged(text: CharSequence?, start: Int,
count: Int, after: Int) {
}
override fun onTextChanged(text: CharSequence?, start: Int, before:
Int, count: Int) {
}
}
onFocusChangeListener = View.OnFocusChangeListener { _, hasFocus ->
if (hasFocus) {
addTextChangedListener(textWatcher)
} else {
removeTextChangedListener(textWatcher)
}
}
}
Вот как вы используете
youEditText.doAfterTextChangedMultipleListenerFix{text->
//do what you want
}
И, наконец, вы должны очистить фокус, прежде чем уведомлять об изменении адаптера.