Android - EditText TextWatcher 保持小写,不能保持插入符号(输入光标)位置

Android - EditText TextWatcher to keep lowercase, cannot keep caret (input cursor) position

我有一个表单,用户可以在其中在 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); 导致整个 Watcher 递归 运行。我可以通过添加 txtEmail.setSelection(temp.length()); 并使用 if 转义递归循环来控制插入符号的位置以到达 EditText 的末尾,但我找不到将插入符号保持在特定点的方法。例如,如果我写了“myema(il missing)@something.com 并想返回以在输入的每个字母处进行更正,插入符号位于字符串的末尾。

现在是奇怪的部分。我尝试将插入符号的位置保持在 beforeTextChanged(...) 之前或 onTextChanged(...) 中。在我输入内容的那一刻,插入符号的位置在每种情况下都会正确更改。不过,在我们进入递归调用的那一刻,插入符号的位置被报告为 0。我猜想当我实际键入时,插入符号的移动也被记录了,但是因为在递归调用中没有插入符号移动而不是 "pasting" EditText 中的文本我没有位置变化。

因此问题来了:如何保持插入符号的位置?我的想法是实际上对文本进行子字符串化。将所有内容都放入插入符号,使用 txtEmail.setSelection(temp.length()); 进行更改,然后在 else 步骤中附加字符串的其余部分(目前尚未尝试)。有没有其他 (simpler/efficient) 方法可以用内置工具处理它?

提前致谢!

也许您遇到了困难。为什么不使用

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

用于电子邮件的 EditText?如果您允许用户输入任何其他字符,只需将这些字符添加到 android:digits 字段即可。

OP 编辑​​:

在按照 Alexandru 的建议(见评论)注销并再次注册 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();
            }
        }
    });