Search code examples
androidandroid-edittextandroid-textwatcher

TextWatcher afterTextChanged()'s Editable.replace() does not replace text in EditText properly


In the Android app, I have an EditText which should replace certain strings with values from app data.

for e.g. if user types $username it should get replaced with the name of whichever user is currently logged in.

The Editable parameter in afterTextChanged() method of TextWatcher applied on EditText replaces the $username with correct value but the problem is that after the $username is replaced with actual username if I press any character after that it is appended with username followed by pressed character.

e.g.

Say current logged in username is Joe

a. if the input is Hi this is @username

b. afterTextChanged() changes it to Hi this is Joe

c. Now if I press any other character(say I press g OR space) then text in EditText changes to Hi this is Joeusernameg OR Hi this is Joeusername

How do I get output as in step b?

etTemplateMessage.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) {
            final String strUserNameCode = "$username";
            String text = s.toString();
            if(text.contains(strUserNameCode)){
                int startIndex = text.indexOf(strUserNameCode);
                int endIndex = startIndex + strUserNameCode.length();
                Editable profileName = new SpannableStringBuilder(FileUtil.getUTF8String(settingDTO.getProfileName()));
                s.replace(startIndex, endIndex, profileName);
            }

        }
    });

Solution

  • on your afterTextChange method you should set text to the edit text. And String has replace(CharSequence old, CharSequence new) method you can also use it. like this,

    PublishSubject publishSubject = PublishSubject.create();
            publishSubject.debounce(200, TimeUnit.MILLISECONDS)
                    .subscribeOn(Schedulers.newThread())
                    .observeOn(AndroidSchedulers.mainThread())
                    .subscribe(query -> onTextChanged(query.toString()));
    
    
    void onTextChanged(String text){
     final String strUserNameCode = "$username";
         etTemplateMessage.setText(text.replace(strUserNameCode, FileUtil.getUTF8String(settingDTO.getProfileName())));
    }
    

    and on your aftertextChange method call publishSubject.onNext( s.toString())

    • Note that you can achieve this with RxJava.