开发者

How can I implement digit grouping input mask using InputFilter?

开发者 https://www.devze.com 2023-04-07 05:36 出处:网络
I am using InputFilter class to make a masked EditText supporting digit grouping. For example when the user inserts\" 12345\" I want to show \"12,345\" in EditText. How can I implement it?

I am using InputFilter class to make a masked EditText supporting digit grouping. For example when the user inserts" 12345" I want to show "12,345" in EditText. How can I implement it?

This is my incomplete code:

        InputFilter IF = new InputFilter() {
        @Override
        public CharSequence filter(CharSequence source, int start, int end,
                Spanned dest, int dstart, int dend) {

            for (int i = start; i < end; i++) {
                if (!Character.isLetterOrDigit(source.charAt(i))) {
                    return "";
                }
            }
            if (dest.length() > 0 && dest.length() % 3 == 0)
            {
                return "," + source;
            }
            return null;
        }
    };
    edtRadius.setFilters(new Inpu开发者_StackOverflowtFilter[] { IF });

Is there any other way to implement this kind of input mask?


This an improvement on the response from @vincent. It adds checks on deleting spaces in a number in the format 1234 5678 9190 so when trying to delete a space it just moves the cursor backon character to the digit before the space. It also keeps the cursor in the same relative place even if spaces are inserted.

    mTxtCardNumber.addTextChangedListener(new TextWatcher() {

        private boolean spaceDeleted;

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

        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            // check if a space was deleted
            CharSequence charDeleted = s.subSequence(start, start + count);
            spaceDeleted = " ".equals(charDeleted.toString());
        }

        public void afterTextChanged(Editable editable) {
            // disable text watcher
            mTxtCardNumber.removeTextChangedListener(this);

            // record cursor position as setting the text in the textview
            // places the cursor at the end
            int cursorPosition = mTxtCardNumber.getSelectionStart();
            String withSpaces = formatText(editable);
            mTxtCardNumber.setText(withSpaces);
            // set the cursor at the last position + the spaces added since the
            // space are always added before the cursor
            mTxtCardNumber.setSelection(cursorPosition + (withSpaces.length() - editable.length()));

            // if a space was deleted also deleted just move the cursor
            // before the space
            if (spaceDeleted) {
                mTxtCardNumber.setSelection(mTxtCardNumber.getSelectionStart() - 1);
                spaceDeleted = false;
            }

            // enable text watcher
            mTxtCardNumber.addTextChangedListener(this);
        }

        private String formatText(CharSequence text)
        {
            StringBuilder formatted = new StringBuilder();
            int count = 0;
            for (int i = 0; i < text.length(); ++i)
            {
                if (Character.isDigit(text.charAt(i)))
                {
                    if (count % 4 == 0 && count > 0)
                        formatted.append(" ");
                    formatted.append(text.charAt(i));
                    ++count;
                }
            }
            return formatted.toString();
        }
    });


In case you're still searching, I ran into this problem the last day, and found that using a TextWatcher is the best (still not really good) option. I had to group digits of credit card numbers.

someEditText.addTextChagedListener(new TextWatcher()
{   
    //According to the developer guide, one shall only edit the EditText's
    //content in this function. 
    @Override
    public void afterTextChanged(Editable text)
    {
        //You somehow need to access the EditText to remove this listener
        //for the time of the changes made here. This is one way, but you
        //can create a proper TextWatcher class and pass the EditText to
        //its constructor, or have the EditText as a member of the class
        //this code is running in (in the last case, you simply have to
        //delete this line).
        EditText someEditText = (EditText) findViewById(R.id.someEditText);

        //Remove listener to prevent further call due to the changes we're
        //about to make (TextWatcher is recursive, this function will be
        //called again for every change you make, and in my experience,
        //replace generates multiple ones, so a flag is not enough.
        someEditText.removeTextChangedListener(this);

        //Replace text with processed the processed string.
        //FormatText is a function that takes a CharSequence (yes, you can
        //pass the Editable directly), processes it the way you want, then
        //returns the result as a String. 
        text.replace(0, text.length(), FormatText(text));

        //Place the listener back
        someEditText.addTextChangedListener(this);
    }

    @Override
    public void beforeTextChaged(CharSequence s, int start, int count, int after)
    {

    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count)
    {

    }
});

My formatting function for the credit card numbers looked like this:

String FormatText(CharSequence text)
{
    StringBuilder formatted = new StringBuilder();

    int count = 0;
    for (int i = 0; i < text.length(); ++i)
    {
        if (Character.isDigit(text.charAt(i)))
        {
            //You have to be careful here, only add extra characters before a
            //user-typed character, otherwise the user won't be able to delete
            //with backspace, since you put the extra character back immediately.
            //However, this way, my solution would put a space at the start of
            //the string that I don't want, hence the > check.
            if (count % 4 == 0 && count > 0)
                formatted.append(' ');
            formatted.append(text.charAt(i));
            ++count;
        }
    }

    return formatted.toString();
}

You might have to mind other issues as well, since this solution actually rewrites the EditText's content every time a change is made. For example, you should avoid processing characters you inserted yourself (that is an additional reason for the isDigit check).


use simple function:

public String digit_grouping(String in_digit){

    String res = "";

    final int input_len = in_digit.length();
    for(int i=0 ; i< input_len ; i++)
    {

        if( (i % 3 == 0) && i > 0 )
            res = "," + res;

        res = in_digit.charAt(input_len - i - 1) + res;

    }

    return res;
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号