69

我有一个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已经存在的指针。

4

13 回答 13

52

没有办法EditText直接使用当前接口来做到这一点。我看到两种可能的解决方案:

  1. 重新设计您的应用程序,以便您始终知道TextWatcher添加到特定EditText实例的内容。
  2. 扩展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>
于 2011-06-07T19:39:37.880 回答
30

您可以从 EditText 中删除 TextWatcher。首先,我建议您将 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
    }
};

之后,您将能够更简单地设置 TextWath:

editText.addTextChangedListener(yourTextWatcher);

比你可以像这样删除 TextWatcher :

editText.removeTextChangedListener(yourTextWatcher);

并根据需要设置另一个。

于 2013-02-12T13:46:53.320 回答
12

我也花了很多时间寻找解决方案,最后在下面的标签的帮助下解决了。它将通过从 convertView 的标记中获取引用来删除以前的 TextWatcher 实例。它完美地解决了这个问题。在您的 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;
        }
    }

然后在您覆盖的公共视图 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");

    }
}

它会通过从 convertView 的标签中获取引用来删除以前的 TextWatcher 实例

于 2015-01-20T09:18:40.343 回答
5

将当前的 textwatcher 保存在 viewholder 中,您可以找到要删除的那个。

于 2014-04-12T00:26:24.293 回答
5

我在 RecyclerView 中遇到了很多 EditTexts 的类似问题。我通过反思解决了它。ReflectionTextWatcher.removeAll(your_edittext)在绑定视图之前调用。这段代码找到所有 TextWatchers 并将它们从名为“mListeners”的本地 EditText 列表中删除。

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

我希望,这会对某人有所帮助。

于 2020-11-11T23:06:04.767 回答
4

很久没有问这个问题了,但有人可能会觉得这很有用。Recyclerview 中 TextWatcher 的问题是我们必须确保在视图被回收之前将其删除。否则,我们会丢失 TextWatcher 的实例,并且在 OnBindViewHolder() 中调用 removeTextChangedListener(textWatcher) 只会删除 TextWatcher 的当前实例。

我解决这个问题的方法是在 FocusChangedListener 中添加 TextChangedListener:

editText.setOnFocusChangeListener(new OnFocusChangeListener() {          
public void onFocusChange(View v, boolean hasFocus) {
    if(hasFocus) {
        editText.addTextChangedListener(textWatcher)
    }
    else{
        editText.removeTextChangedListener(textWatcher)
    }
  }
});

这样我确定当editText没有焦点时,textwatcher被删除,并在它有焦点时再次添加。因此,当 recyclerview 被回收时,editText 将删除任何 textChangeListener。

于 2021-02-05T01:14:44.370 回答
1

正如您在此处看到的:TextView 的 CodeSearch无法删除所有侦听器。唯一的方法是提供你用来注册它的观察者。

我还不完全明白为什么还有其他听众已经注册。但是,您可以将 EditText 子类化,覆盖 addTextChangedListener(..) 并在其中保留自己添加的所有引用的副本,然后委托给超类实现。然后,您还可以提供删除所有侦听器的附加方法。

如果您需要进一步的解释,请与我们联系。

于 2011-06-07T19:39:38.503 回答
1

如果像我一样处理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()
    }
}
于 2019-12-04T12:32:47.907 回答
1

我在没有扩展 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();
}
于 2018-02-20T15:39:34.097 回答
1

我在使用 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 类将关心您的 textview 上的多个事件。您还可以更改 textchange 事件的类。这是相同的原理。我希望这将有所帮助。

再见,nxexo007

于 2016-12-03T09:35:47.903 回答
0

为什么不使用 setTag() 将 textwatcher 引用附加到 edittext 本身

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

在 /values 下的 ids.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <item name="textWatcherTag" type="id" />
</resources>
于 2021-04-11T14:54:45.180 回答
0

我在类似的问题上苦苦挣扎。我通过保存对我的 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));
}
于 2018-05-14T22:48:39.417 回答
0

我为删除文本观察者所做的非常简单。我创建了一个数组来放置我的文本观察器:

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 方法在 afterTextChanged 上使用不同的检查器(用于检查所有四个 editTexts 的 switchMethod)实例化一个 textWatcher。

然后,每当我删除一个文本观察器时,我只是从数组中引用了一个:

etPin4.removeTextChangedListener(textWatchers[CURRENT_PIN_CHECK]);

在调试时检查 editText 的侦听器大小:

在此处输入图像描述

它被删除了!这解决了我的问题!

于 2016-03-02T00:50:42.443 回答