Search code examples
androidandroid-handlertextwatcherandroid-textwatcher

TextWatcher - cursor goes back to the start of EditText


I've got the following TextWatcher definition:

   _textWatcher = 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(final Editable s) {
            if (!s.toString().equals(_prev)) {
                afterTextHandler.removeCallbacks(work);
                work = new Runnable() {
                    @Override
                    public void run() {                            
                        if (!s.toString().equals(_prevQuoteAmount)) {
                            _prev = s.toString();
                            doSomething();
                        }
                        _et.append("");                            
                    }
                };
                afterTextHandler.postDelayed(work, 1000);
            }
            _et.append("");                
        }
    };

Everything works as expected (sometimes doSomething() executes and sometimes not, so its execution isn't relevant to the question), but after the user types some text in the EditText component that has this watcher attached to it, the cursor of the text goes back to the beginning of the EditText.

I've got the following log that is shown after onTextChanged() execution (or its handler run() execution):

12-08 13:10:55.777 1855-1855/com.android.inputmethod.latin I/LatinIME: Starting input. Cursor position = 4,4

Why is the cursor of EditText placed in the beginning of the EditText right after onTextChanged() execution?

Edit 1: _et.append("") is a hack that should bring the cursor to the last place of the text. It doesn't work here (though it works on other occasions).

Edit 2: Requested knowledge about doSomething:

    if (isReady()) {
        Sender objSender = new Sender();
        objSender.setParam1(_tvParam1.getText().toString());
        objSender.setParam2(_tvParam2.getText().toString());
        objSender.setParam3(_et.getText().toString());
        Service.startActionSend(getActivity(), quote);
    }

isReady() - checks conditions (_tvParam1, _tvParam2, _et) before making a network request. Service.startActionSend() - making async network request (using IntentService).

Edit 3: The issue happens only when entering the handler's run() method.


Solution

  • you can use this function to get the cursor to be used in setSelection of the edit Text also

    private fun getNewCursorPosition(digitCountToRightOfCursor : Int, numberString : String) : Int{
        var position = 0
        var c = digitCountToRightOfCursor
        for (i in numberString.reversed()) {
            if (c == 0)
                break
    
            if (i.isDigit())
                c --
            position ++
    
    
        }
        return numberString.length - position
    }
    

    for the whole sample use

    editText.addTextChangedListener(object : TextWatcher {
            override fun afterTextChanged(p0: Editable?) {
            }
    
            override fun beforeTextChanged(p0: CharSequence?, start: Int, count: Int, after: Int) {
                beforeText = p0.toString()
            }
    
            override fun onTextChanged(p0: CharSequence?, start: Int, before: Int, count: Int) {
                
                if (p0 == null)
                    return
                // 1. get cursor position : p0 = start + before
                val initialCursorPosition = start + before
                //2. get digit count after cursor position : c0
                val numOfDigitsToRightOfCursor = getNumberOfDigits(beforeText.substring(initialCursorPosition,
                        beforeText.length))
                val newAmount = formatAmount(p0.toString())
                editText.removeTextChangedListener(this)
                editText.setText(newAmount)
                //set new cursor position
                editText.setSelection(getNewCursorPosition(numOfDigitsToRightOfCursor, newAmount))
                editText.addTextChangedListener(this)
    
    
    
            }
    
        })
    

    also you may need this function

     private fun getNumberOfDigits(@NonNull text : String) : Int{
        var count = 0
        for (i in text)
            if (i.isDigit())
                count++
        return count
    }
    

    Reference :FULL Code Description