14

我正在开发一个 android 应用程序,并且我有一个 EditText,用户可以在其中输入数字。我想使用不同的货币格式(比如##、##、###)来格式化数字,并且我想即时进行,即当用户输入每个数字时(而不是按下输入时)。我四处搜索,发现了TextWatcher,我首先发现它很有前途,但结果证明它绝对是一种痛苦。我在只有软键盘的 HTC Desire 手机上调试我的代码。

现在我想在用户按下数字(0 到 9)、del(退格)键和 enter 键时获得回调。从我的测试中我发现了这些(至少在我的手机上)

1) 当用户按下 del 或 enter 键时调用 editText onKeyListener。当用户按下回车键时,一次回车会调用两次 onKey 函数(我相信这是针对 ACTION_UP 和 ACTION_DOWN 的)。当用户按下 del 时,onKey 被调用一次(仅用于 ACTION_DOWN),我不知道为什么。当用户按下我也无法理解的任何数字(0 到 9)时,永远不会调用 onKey。

2) 每当用户按下任何数字(0 到 9)键时,都会调用 TextWatchers 3 个回调函数(beforeTextChanged、onTextChanged、afterTextChanged)。所以我想通过一起使用 TextWatcher 和 onKeyListener 我可以获得我需要的所有回调。

现在我的问题是这些..

1)首先在我的 HTC 软键盘中有一个键(一个带有向下箭头的键盘符号),当我点击它时,键盘会退出而不给任何回调。我仍然无法相信 android 让用户编辑字段并在不让程序处理(保存)编辑的情况下辞职。现在我的editText显示一个值而我的对象有另一个值(我在输入时保存用户编辑,并通过使用对象中的值重置editText值来处理键盘上的回击,但我对此键盘按下键没有答案) .

2)其次,我想在用户输入新数字后格式化数字。假设我在 editText 上已经有 123 并且用户输入了 4,我希望我的 editText 显示 1,234。我在 onTextChanged() 和 afterTextChanged() 上得到了完整的数字,我可以格式化数字并在任何这些回调中将其放回 editText。我应该使用哪一个?哪个是最佳实践?

3)第三个是最关键的问题。当应用程序启动时,我将当前对象值放在 editText 中。假设我在 onResume() 上放了 123,当用户输入一个数字(比如 4)时,我希望它是 1234。但是在我的 onTextChanged 回调中,我得到的是 4123。当我再按一个键(比如 5)时,我是得到 45123。因此对于用户输入,editText 光标指向文本的结尾。但是当手动设置值时,editText 光标似乎没有更新。我相信我必须在 textWatcher 回调中做一些事情,但我不知道我应该做什么。

我在下面发布我的代码。

public class AppHome extends AppBaseActivity {
    private EditText ed = null;
    private NumberFormat amountFormatter = null;
    private boolean  isUserInput = true;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.app_home_screen);

        ed = (EditText)findViewById(R.id.main_amount_textfield);
        amountFormatter = new DecimalFormat("##,##,###");


        ed.setOnKeyListener(new View.OnKeyListener() {
            @Override
            public boolean onKey(View v, int keyCode, KeyEvent event) {
                if(event.getAction() == KeyEvent.ACTION_DOWN) 
                    return true;
                String strippedAmount = ed.getText().toString().replace(",", "");
                if(keyCode == KeyEvent.KEYCODE_DEL){
                    //delete pressed, strip number of comas and then delete least significant digit.
                    strippedAmount = strippedAmount.substring(0, strippedAmount.length() - 1);
                    int amountNumeral = 0;
                    try{
                        amountNumeral = Integer.parseInt(strippedAmount);
                    } catch(NumberFormatException e){
                    }
                    myObject.amount = amountNumeral;
                    isUserInput = false;
                    setFormattedAmount(amountNumeral,ed.getId());
                }else if(keyCode == KeyEvent.KEYCODE_ENTER){
                    //enter pressed, save edits and resign keyboard
                    int amountNumeral = 0;
                    try{
                        amountNumeral = Integer.parseInt(strippedAmount);
                    } catch(NumberFormatException e){
                    }
                    myObject.amount = amountNumeral;
                    isUserInput = false;
                    setFormattedAmount(myObject.amount,ed.getId());
                    //save edits
                    save();
                    //resign keyboard..
                    InputMethodManager in = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                    in.hideSoftInputFromWindow(AppHome.this.getCurrentFocus().getWindowToken(),InputMethodManager.HIDE_NOT_ALWAYS);
                }
                return true;
            }
        });

        TextWatcher inputTextWatcher = new TextWatcher() {
            public void afterTextChanged(Editable s) { 
                if(isUserInput == false){
                    //textWatcher is recursive. When editText value is changed from code textWatcher callback gets called. So this variable acts as a flag which tells whether change is user generated or not..Possibly buggy code..:(
                    isUserInput = true;
                    return;
                }
                String strippedAmount = ed.getText().toString().replace(",", "");
                int amountNumeral = 0;
                try{
                    amountNumeral = Integer.parseInt(strippedAmount);
                } catch(NumberFormatException e){
                }
                isUserInput = false;
                setFormattedAmount(amountNumeral,ed.getId());
            }

            public void beforeTextChanged(CharSequence s, int start, int count, int after){
            }
            public void onTextChanged(CharSequence s, int start, int before, int count) {
            }
        };

        ed.addTextChangedListener(inputTextWatcher);
    }//end of onCreate...

    public void setFormattedAmount(Integer amount, Integer inputBoxId){
        double amountValue = 0;
        String textString =null;
        TextView amountInputBox = (TextView) findViewById(inputBoxId);

        amountValue = Double.parseDouble(Integer.toString(amount));
        textString = amountFormatter.format(amountValue).toString();
        amountInputBox.setText(textString);
    }
}

我知道这是一个大问题,但我正在处理同样的问题 2 天。我是 android 新手,仍然无法相信没有简单的方法可以即时处理 textEdit 数据(我在 iphone 上轻松地做了同样的事情)。谢谢大家

编辑:使用输入过滤器后

InputFilter filter = new InputFilter() { 
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) { 
            String strippedAmount = dest.toString() + source;
            strippedAmount = strippedAmount.replace(",", "");

        int amountNumeral = 0;
        try{
            amountNumeral = Integer.parseInt(strippedAmount);
        } catch(NumberFormatException e){
        }           
            return amountFormatter.format(amountNumeral).toString(); 
    } 
}; 

ed.setFilters(new InputFilter[]{filter}); 

当应用程序启动时,我在 editText 上放置了 1,234

myObject.amount = 1234;
ed.setText(amountFormatter.format(myObject.amount).toString());

然后当用户单击editText时,键盘弹出,并说用户输入数字6

我得到:61234 我想要:12346

4

3 回答 3

8

好吧,在敲了很多头之后,我找到了解决光标位置问题的方法..我不知道这是否是正确的方法,但我得到了它的工作..

    TextWatcher inputTextWatcher = new TextWatcher() {
        public void afterTextChanged(Editable s) { 
            if(isUserInput == false){
                //textWatcher is recursive. When editText value is changed from code textWatcher callback gets called. So this variable acts as a flag which tells whether change is user generated or not..Possibly buggy code..:(
                isUserInput = true;
                ed.setSelection(ed.getText().length());
                return;
            }
            String strippedAmount = ed.getText().toString().replace(",", "");
            int amountNumeral = 0;
            try{
                amountNumeral = Integer.parseInt(strippedAmount);
            } catch(NumberFormatException e){
            }
            isUserInput = false;
            setFormattedAmount(amountNumeral,ed.getId());
        }

        public void beforeTextChanged(CharSequence s, int start, int count, int after){
        }
        public void onTextChanged(CharSequence s, int start, int before, int count) {
        }
    };
ed.addTextChangedListener(inputTextWatcher);


ed.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            int length          =   ed.getText().length();
            ed.setCursorVisible(true);
            ed.setSelection(length);
        }
    });

ed.setOnKeyListener(new View.OnKeyListener() {
    @Override
    public boolean onKey(View v, int keyCode, KeyEvent event) {
            if(event.getAction() == KeyEvent.ACTION_UP) 
                return true;
            String strippedAmount = ed.getText().toString().replace(",", "");
            if(keyCode == KeyEvent.KEYCODE_DEL){
                //delete pressed, strip number of comas and then delete least significant digit.
                strippedAmount = strippedAmount.substring(0, strippedAmount.length() - 1);
                int amountNumeral = 0;
                try{
                    amountNumeral = Integer.parseInt(strippedAmount);
                } catch(NumberFormatException e){
                }
                isUserInput = false;
                setFormattedAmount(amountNumeral,ed.getId());
                return true;
            }else if(keyCode == KeyEvent.KEYCODE_ENTER){
                //enter pressed, save edits and resign keyboard
                int amountNumeral = 0;
                try{
                    amountNumeral = Integer.parseInt(strippedAmount);
                } catch(NumberFormatException e){
                }
                isUserInput = false;
                setFormattedAmount(amountNumeral,ed.getId());
                //save edits
                //resign keyboard..
                InputMethodManager in = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
                in.hideSoftInputFromWindow(AppHome.this.getCurrentFocus().getWindowToken(),InputMethodManager.HIDE_NOT_ALWAYS);
                return true;
            }
            return false;
    }
});

我所做的是在editText的onClick()上,我强制将光标放在当前EditText文本的末尾,当用户按下任何数字时我也做了同样的事情。希望它可以帮助某人..感谢所有试图提供帮助的人。

于 2011-04-14T03:42:01.540 回答
4

对于 Masked 输入,您可以继承 InputFilter

下面是一个示例 InputFilter 子类,它将所有小写字母大写:

   /**
     * This filter will capitalize all the lower case letters that are added
     * through edits.
     */
    public static class AllCaps implements InputFilter {
        public CharSequence filter(CharSequence source, int start, int end,
                                   Spanned dest, int dstart, int dend) {
            for (int i = start; i < end; i++) {
                if (Character.isLowerCase(source.charAt(i))) {
                    char[] v = new char[end - start];
                    TextUtils.getChars(source, start, end, v, 0);
                    String s = new String(v).toUpperCase();

                    if (source instanceof Spanned) {
                        SpannableString sp = new SpannableString(s);
                        TextUtils.copySpansFrom((Spanned) source,
                                                start, end, null, sp, 0);
                        return sp;
                    } else {
                        return s;
                    }
                }
            }

            return null; // keep original
        }
    }

以上代码取自Android对InputFilter的实现

于 2011-04-13T06:53:35.040 回答
1

经过几个小时的工作,我做了一个电话输入掩码。例如,在输入“123456”后,它会将其转换为“+1 (234) 56”。从任何位置删除任何符号后,光标会移动到正确的位置,而不是开始或结束。

在活动中:

    etPhone.addTextChangedListener(new PhoneWatcher(etPhone));

在班上:

private class PhoneWatcher implements TextWatcher {
    private static final String PHONE_MASK = "+# (###) ###-##-##";
    private final char[] PHONE_MASK_ARRAY = PHONE_MASK.toCharArray();

    private boolean isInTextChanged;
    private boolean isInAfterTextChanged;
    private EditText editText;
    private int shiftCursor;
    private String text;
    private int cursor;

    public PhoneWatcher(EditText editText) {
        super();
        this.editText = editText;
        isInTextChanged = false;
        isInAfterTextChanged = false;
    }

    @Override
    public synchronized void beforeTextChanged(CharSequence s, int start, int count, int after) {
        shiftCursor = after - count;
    }

    @Override
    public synchronized void onTextChanged(CharSequence s, int start, int before, int count) {
        if (!isInTextChanged) {
            isInTextChanged = true;

            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < s.length(); i++) {
                char symbol = s.charAt(i);
                if (symbol >= '0' && symbol <= '9')
                    sb.append(symbol);
            }
            String digits = sb.toString();

            sb.setLength(0);
            int j = 0;
            for (int i = 0; i < digits.length(); i++) {
                char digit = digits.charAt(i);
                while (j < PHONE_MASK_ARRAY.length) {
                    if (PHONE_MASK_ARRAY[j] == '#') {
                        sb.append(digit);
                        j++;
                        break;
                    } else {
                        sb.append(PHONE_MASK_ARRAY[j]);
                        j++;
                    }
                }
            }

            cursor = editText.getSelectionStart();
            text = sb.toString();

            if (shiftCursor > 0) {
                if (cursor > text.length())
                    cursor = text.length();
                else {
                    while (cursor < PHONE_MASK_ARRAY.length && PHONE_MASK_ARRAY[cursor - 1] != '#') {
                        cursor++;
                    }
                }
            } else if (shiftCursor < 0) {
                while (cursor > 0 && PHONE_MASK_ARRAY[cursor - 1] != '#') {
                    cursor--;
                }
            }
        }
    }

    public synchronized void afterTextChanged(Editable s) {
        if (!isInAfterTextChanged) {
            isInAfterTextChanged = true;

            editText.setText(text);
            editText.setSelection(cursor);

            isInTextChanged = false;
            isInAfterTextChanged = false;
        }
    }
}
于 2015-12-17T14:21:48.857 回答