Android - EditText TextWatcher для сохранения нижнего регистра, не может сохранять положение каретки (курсора ввода)

У меня есть форма, в которой пользователь может ввести некоторые данные в поля EditText. Один из этих виджетов EditText предназначен для адреса электронной почты. Я использую TextWatcher, чтобы убедиться, что текст всегда в нижнем регистре, следующим образом:

txtEmail.addTextChangedListener(new TextWatcher()
    {
        String prevString = "";
        boolean delAction = false;

        @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 (s.length() < prevString.length())
                delAction = true;
            else
                delAction = false;
        }

        @Override
        public void afterTextChanged(Editable s)
        {
            if (!delAction)
            {
                String temp = s.toString().toLowerCase();
                if (!temp.equals(prevString))
                {
                    prevString = temp;
                    txtEmail.setText(temp); // Recursive
                    txtEmail.setSelection(temp.length());
                }
            }
            else
            {
                prevString = s.toString();
            }
        }
    });

В onTextChanged(...) я также сравниваю, чтобы убедиться, что удаление работает правильно.

Теперь к проблеме. txtEmail.setText(temp); заставляет весь Наблюдатель работать рекурсивно. Я могу управлять положением каретки, чтобы перейти в конец EditText, добавив txtEmail.setSelection(temp.length()); и выйти из рекурсивного цикла с помощью if, но я не могу найти способ сохранить каретку в определенной точке. Если, например, я написал "myema(ilmissing)@something.com и хочу вернуться, чтобы исправить каждую введенную букву, знак вставки будет стоять в конце строки.

Теперь самое странное. Я попытался сохранить положение каретки перед beforeTextChanged(...) или в onTextChanged(...). В тот момент, когда я что-то набираю, положение каретки в каждом случае корректно меняется. Однако в тот момент, когда мы входим в рекурсивный вызов, положение каретки сообщается как 0. Я предполагаю, что когда я на самом деле печатаю, движение каретки также регистрируется, но поскольку в рекурсивном вызове нет движения каретки, а не «вставка». " текста в EditText я не получаю изменения позиции.

И поэтому вопрос: как я могу сохранить положение каретки? У меня была мысль на самом деле подставить текст. Поместите все в каретку, внесите изменения, бросьте их туда с помощью txtEmail.setSelection(temp.length());, а затем добавьте остальную часть строки на шаге else (пока еще не пробовал). Есть ли другой (более простой/эффективный) способ справиться с этим с помощью встроенных инструментов?

Заранее спасибо!


person MadDim    schedule 20.04.2017    source источник
comment
Зачем ты все равно доводишь до конца?   -  person creativecreatorormaybenot    schedule 20.04.2017


Ответы (1)


Возможно, вы подошли к этому трудным путем. Почему бы не использовать

android:digits="qwertyuiopasdfghjklzxcvbnm_,.@.," 

для EditText для электронной почты? Если вы разрешаете пользователю вводить любые другие символы, просто добавьте их в поле android:digits.

РЕДАКТИРОВАНИЕ OP:

После того, как Александру последовал совету (см. комментарии) отменить регистрацию и снова зарегистрировать TextWatcher, код изменился и работает следующим образом:

    txtEmail.addTextChangedListener(new TextWatcher()
    {
        String prevString = "";
        boolean delAction = false;
        int caretPos = 0;

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after)
        {
            caretPos = txtEmail.getSelectionStart();
        }

        @Override
        public void onTextChanged(CharSequence s, int start, int before, int count)
        {
            if (s.length() < prevString.length())
                delAction = true;
            else
                delAction = false;
        }

        @Override
        public void afterTextChanged(Editable s)
        {
            if (!delAction)
            {
                prevString = s.toString().toLowerCase();
                txtEmail.removeTextChangedListener(this);
                txtEmail.setText(prevString);
                txtEmail.addTextChangedListener(this);
                txtEmail.setSelection(caretPos + 1);
            }
            else
            {
                txtEmail.setSelection(caretPos - 1);
                prevString = s.toString();
            }
        }
    });
person Alexandru Dascălu    schedule 20.04.2017
comment
Самая большая проблема с этим подходом заключается в том, что если я набираю заглавные буквы, символы вообще не регистрируются. Кроме того, мне нужно поместить всю клавиатуру в android:digits="...", чтобы охватить все возможные комбинации в соответствии с формальными определениями адресов электронной почты. . Я долго и упорно искал ту функциональность, которая мне нужна, и я думаю, что это единственный способ добиться этого. Хотя хотелось бы оказаться неправым ;) - person MadDim; 20.04.2017
comment
Ага, так что, если пользователь вводит заглавную букву «А», вы должны показывать строчную «а»? Чтобы избежать установки курсора вручную, не поможет ли просто отменить регистрацию TextWatcher перед установкой текста с помощью txtEmail.setText(temp), а затем снова зарегистрировать его? Таким образом, я думаю, вы любезно избегаете рекурсивного вызова. - person Alexandru Dascălu; 20.04.2017
comment
Аааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааааа...Никогда не думали отменить регистрацию TextWatcher. На самом деле это казалось очень странным, отмена регистрации внутри, когда я прочитал ваш комментарий! Судя по всему, Java/Android это не волнует (иногда this действительно волшебно!!!) и это работает!!! Если вы не возражаете, я отредактирую ваш ответ с внесенными изменениями и отмечу его. - person MadDim; 20.04.2017
comment
Конечно, рад, что помогло. На самом деле это совсем не странно. Вы можете ознакомиться с исходным кодом для TextView, который является родительским классом для EditText. Вы можете видеть, что TextWatcher — это обычный слушатель, ничего особенного. Вы можете проверить здесь шаблон слушателя (наблюдателя), если вы не знакомы с этим. - person Alexandru Dascălu; 20.04.2017