Search code examples
androidandroid-edittextandroid-softkeyboardtextwatcherandroid-textwatcher

TextWatcher onTextChanged not working with soft keyboard auto-complete / suggested words


I'm Implementing a TextWatcher on an EditText to find and underline a series of keywords within the text whenever a new character is entered by the user. However when a suggested / auto-complete word on the soft keyboard is selected, instead of adding the suggested word to the Edittext and then calling the onTextChanged function the half complete word is deleted. I found this rather strange as entering individual characters activates the onTextChanged function just fine. Any help would be greatly appreciated.

PS. if anyone knows a better way to process an EditText on the fly please let me know.

Code:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_codify_test);
    final EditText editText = (EditText) findViewById(R.id.editText_codifyTest);
    editText.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) {
            if (!checked) { //stop infinite loop
                checked = true;
                cursorPosition = editText.getSelectionStart(); //get cursor position before text modification
                codifyText(editText);
            } else {
                checked = false;
            }
        }
        @Override
        public void afterTextChanged(Editable s) {
        }
    });
}

//Find and underline keywords
private void codifyText(EditText editText) {
    String plainText = editText.getText().toString() + " ";
    int prevWhiteSpace = 0;
    final Context context = this;
    SpannableString codifiedText = new SpannableString(plainText.substring(0, plainText.length() - 1));
    if (codifiedText.length() == 0) return;

    for (int i = 0; i < plainText.length(); i ++){
        if (Character.isWhitespace(plainText.charAt(i))){
            String currWord = plainText.substring(prevWhiteSpace, i);
            if (isKeyWordInDataBase(currWord)) {
                ClickableSpan clickableSpan = new ClickableSpan() {
                    @Override
                    public void onClick(View view) {

                    }
                };
            codifiedText.setSpan(clickableSpan, prevWhiteSpace, i, 0);
            }
            prevWhiteSpace = i + 1;
        }
    }
    editText.setMovementMethod(LinkMovementMethod.getInstance());
    editText.setText(codifiedText, TextView.BufferType.SPANNABLE);
    editText.setSelection(cursorPosition); //set cursor to position prior to edit
}

Solution

  • Its better to go for afterTextChanged method.
    In the following sample, the entered text is retrieved and handled using a Handler for the further process

        EditText text1;
        StringBuffer previousChar=new StringBuffer();
        @Override
        protected void onCreate(Bundle savedInstanceState) {
        setContentView(R.layout.activity_main);
        text1=(EditText)findViewById(R.id.editText);
        text1.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(!previousChar.toString().equals(s.toString())){
                            Message msg=new Message();
                            msg.obj=s.toString();
                            localHandler.sendMessage(msg);
                            previousChar=new StringBuffer(s.toString());
                            Log.i("TAG", "TextEntered = "+s);
                        }
                    }
                });}
    

    And in the handler

    private Handler localHandler = new Handler(){
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                String value=(String)msg.obj;
                //Your logic with the text entered that is retrieved 
            }
        };