How to remove all listeners added with addTextChangedListener

前端 未结 11 1571
谎友^
谎友^ 2020-11-27 16:24

I have a ListView where each row has an EditText control. I want to add a TextChangedListener to each row; one that contains extra dat

相关标签:
11条回答
  • 2020-11-27 16:49

    I struggled with a similar problem. I solved it by saving references to my textWatchers in an 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));
    }
    
    0 讨论(0)
  • 2020-11-27 16:50

    What I did to remove text watchers is very simple. I created an array to put my textwatchers:

    final TextWatcher[] textWatchers = new TextWatcher[3];
    

    I added them in:

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

    My returnTextWatcherCheckPIN method instantiates a textWatcher with a different checker (switchMethod to check all four editTexts) on afterTextChanged.

    Then whenever I remove a text watcher I just referenced the one from the array:

    etPin4.removeTextChangedListener(textWatchers[CURRENT_PIN_CHECK]);
    

    Check the listeners size of the editText on debug:

    It's removed! That solved my problem!

    0 讨论(0)
  • 2020-11-27 16:51

    If one, like me, deals with ViewHolder, then simply saving a reference to a text watcher upon its creation will not help. Upon reuse the view will get to some other ViewHolder which would not have a reference to that old text watcher, thus one won't be able to delete it.

    Personally i chose to solve problem like @inazaruk, though updated code to Kotlin + renamed class to better reflect it's purpose.

    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()
        }
    }
    
    0 讨论(0)
  • 2020-11-27 16:53

    I resolved this situation without extend TextView class.

    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();
    }
    
    0 讨论(0)
  • 2020-11-27 16:58

    I struggled with a similar problem with a lot of EditTexts in RecyclerView. I solved it by reflection. Call ReflectionTextWatcher.removeAll(your_edittext) before bind views. This piece of code finds all TextWatchers and removes them from the local EditText's list called "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;
        }
    }
    

    I hope, this will help someone.

    0 讨论(0)
  • 2020-11-27 17:01

    There is no way to do this using current EditText interface directly. I see two possible solutions:

    1. Redesign your application so you always know what TextWatcher are added to particular EditText instance.
    2. Extend EditText and add possibility to clear all watchers.

    Here is an example of second approach - 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;
            }
        }
    }
    

    And here is how you can use ExtendedEditText in xml layouts:

    <?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>
    
    0 讨论(0)
提交回复
热议问题