development

안드로이드의 편집 텍스트에서 신용 카드 포맷

big-blog 2020. 12. 31. 23:20
반응형

안드로이드의 편집 텍스트에서 신용 카드 포맷


EditText형식으로 입력을 받아들이는 방법 :

4digit 4digit 4digit 4digit 

신용 카드 번호를 허용하기 위해 사용자 정의 형식 편집 텍스트 입력 android를 시도 했지만 불행히도 공백을 삭제할 수 없습니다. 공백이있을 때마다 삭제할 ​​수 없었습니다. 문제를 찾는 데 도움을주세요.


데모-작동 원리

github.com의 예

늦은 답변이지만 누군가에게 도움이 될 것 같습니다.

    cardNumberEditText.addTextChangedListener(new TextWatcher() {

        private static final int TOTAL_SYMBOLS = 19; // size of pattern 0000-0000-0000-0000
        private static final int TOTAL_DIGITS = 16; // max numbers of digits in pattern: 0000 x 4
        private static final int DIVIDER_MODULO = 5; // means divider position is every 5th symbol beginning with 1
        private static final int DIVIDER_POSITION = DIVIDER_MODULO - 1; // means divider position is every 4th symbol beginning with 0
        private static final char DIVIDER = '-';

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            // noop
        }

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

        @Override
        public void afterTextChanged(Editable s) {
            if (!isInputCorrect(s, TOTAL_SYMBOLS, DIVIDER_MODULO, DIVIDER)) {
                s.replace(0, s.length(), buildCorrectString(getDigitArray(s, TOTAL_DIGITS), DIVIDER_POSITION, DIVIDER));
            }
        }

        private boolean isInputCorrect(Editable s, int totalSymbols, int dividerModulo, char divider) {
            boolean isCorrect = s.length() <= totalSymbols; // check size of entered string
            for (int i = 0; i < s.length(); i++) { // check that every element is right
                if (i > 0 && (i + 1) % dividerModulo == 0) {
                    isCorrect &= divider == s.charAt(i);
                } else {
                    isCorrect &= Character.isDigit(s.charAt(i));
                }
            }
            return isCorrect;
        }

        private String buildCorrectString(char[] digits, int dividerPosition, char divider) {
            final StringBuilder formatted = new StringBuilder();

            for (int i = 0; i < digits.length; i++) {
                if (digits[i] != 0) {
                    formatted.append(digits[i]);
                    if ((i > 0) && (i < (digits.length - 1)) && (((i + 1) % dividerPosition) == 0)) {
                        formatted.append(divider);
                    }
                }
            }

            return formatted.toString();
        }

        private char[] getDigitArray(final Editable s, final int size) {
            char[] digits = new char[size];
            int index = 0;
            for (int i = 0; i < s.length() && index < size; i++) {
                char current = s.charAt(i);
                if (Character.isDigit(current)) {
                    digits[index] = current;
                    index++;
                }
            }
            return digits;
        }
    });

이것은 시작 문자열 / 끝 문자열 / 중간 문자열 편집에서 완벽하게 작동 하며 붙여 넣기도 완벽하게 작동합니다.


'OK'인 여러 답변을 찾은 후. .NET Framework와 올바르게 독립적으로 작동하도록 설계된 더 나은 TextWatcher로 이동했습니다 TextView.

TextWatcher 클래스는 다음과 같습니다.

/**
 * Formats the watched EditText to a credit card number
 */
public static class FourDigitCardFormatWatcher implements TextWatcher {

    // Change this to what you want... ' ', '-' etc..
    private static final char space = ' ';

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

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

    @Override
    public void afterTextChanged(Editable s) {
        // Remove spacing char
        if (s.length() > 0 && (s.length() % 5) == 0) {
            final char c = s.charAt(s.length() - 1);
            if (space == c) {
                s.delete(s.length() - 1, s.length());
            }
        }
        // Insert char where needed.
        if (s.length() > 0 && (s.length() % 5) == 0) {
            char c = s.charAt(s.length() - 1);
            // Only if its a digit where there should be a space we insert a space
            if (Character.isDigit(c) && TextUtils.split(s.toString(), String.valueOf(space)).length <= 3) {
                s.insert(s.length() - 1, String.valueOf(space));
            }
        }
    }
}

그런 다음 다른 모든 것과 마찬가지로 TextView에 추가합니다 TextWatcher.

{
  //...
  mEditTextCreditCard.addTextChangedListener(new FourDigitCardFormatWatcher()); 
}

이렇게하면 공간이 현명하게 자동 삭제되어 사용자가 편집 할 때 실제로 더 적은 키 입력을 수행 할 수 있습니다.

경고

당신이 사용하는 경우 inputType="numberDigit"'-'를 사용할 수 없게됩니다이 내가 사용하는 것이 좋습니다 있도록하고, ''문자를 inputType="phone". 이것은 다른 문자를 활성화하지만 사용자 정의 입력 필터를 사용하면 문제가 해결됩니다.


나는 그것을 더 강력하게 만들기 위해 Chris Jenkins 답변을 수정했습니다. 이를 통해 사용자가 텍스트 중간을 편집하더라도 공백 문자는 여전히 올바르게 삽입되고 잘못된 위치에서 자동으로 제거됩니다.

이 작업이 올바르게 작동하도록하려면 EditText속성이 다음과 같이 설정되어 있는지 확인하십시오 (의 공백에 유의하십시오 digits).

android:digits="01234 56789"
android:inputType="number"
android:maxLength="19"

그렇다면 여기에 TextWatcher필요한 것이 있습니다. 익명 클래스는 EditText.

    yourTextView.addTextChangedListener(new TextWatcher() {
        private static final char space = ' ';

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

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

        @Override
        public void afterTextChanged(Editable s) {
            // Remove all spacing char
            int pos = 0;
            while (true) {
                if (pos >= s.length()) break;
                if (space == s.charAt(pos) && (((pos + 1) % 5) != 0 || pos + 1 == s.length())) {
                    s.delete(pos, pos + 1);
                } else {
                    pos++;
                }
            }

            // Insert char where needed.
            pos = 4;
            while (true) {
                if (pos >= s.length()) break;
                final char c = s.charAt(pos);
                // Only if its a digit where there should be a space we insert a space
                if ("0123456789".indexOf(c) >= 0) {
                    s.insert(pos, "" + space);
                }
                pos += 5;
            }
        }
    });

다음은 정규 표현식을 사용하는 더 깨끗한 솔루션입니다. 정규식은 비효율적 일 수 있지만이 경우에는 키를 누를 때마다 처리가 발생하더라도 최대 19 자의 문자열을 처리하므로 충분합니다.

editTxtCardNumber.addTextChangedListener(new TextWatcher() {

    @Override
    public void onTextChanged(CharSequence s, int arg1, int arg2,
            int arg3) { }

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

    @Override
    public void afterTextChanged(Editable s) {
        String initial = s.toString();
        // remove all non-digits characters
        String processed = initial.replaceAll("\\D", "");
        // insert a space after all groups of 4 digits that are followed by another digit
        processed = processed.replaceAll("(\\d{4})(?=\\d)", "$1 ");
        // to avoid stackoverflow errors, check that the processed is different from what's already
        //  there before setting
        if (!initial.equals(processed)) {
            // set the value
            s.replace(0, initial.length(), processed);
        }

    }

});

내 솔루션을 목록에 추가하고 있습니다. 내가 아는 한, 단점은 없습니다. 중간에서 편집, 공백 문자 삭제, 복사 및 붙여 넣기 등을 수행 할 수 있습니다.

문자열의 어느 곳에서나 편집이 이루어 지도록하고 커서 위치를 유지하기 위해 Editable이 순회되고 모든 공백 (있는 경우)이 하나씩 제거됩니다. 그런 다음 새 공백이 적절한 위치에 추가됩니다. 이렇게하면 내용에 대한 변경 사항과 함께 커서가 이동합니다.

import java.util.LinkedList;


import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;


/**
 * Formats the watched EditText to groups of characters, with spaces between them.
 */
public class GroupedInputFormatWatcher implements TextWatcher {

    private static final char SPACE_CHAR = ' ';
    private static final String SPACE_STRING = String.valueOf(SPACE_CHAR);
    private static final int GROUPSIZE = 4;

    /**
     * Breakdown of this regexp:
     * ^             - Start of the string
     * (\\d{4}\\s)*  - A group of four digits, followed by a whitespace, e.g. "1234 ". Zero or more times.
     * \\d{0,4}      - Up to four (optional) digits.
     * (?<!\\s)$     - End of the string, but NOT with a whitespace just before it.
     * 
     * Example of matching strings:
     *  - "2304 52"
     *  - "2304"
     *  - ""
     */
    private final String regexp = "^(\\d{4}\\s)*\\d{0,4}(?<!\\s)$";
    private boolean isUpdating = false;

    private final EditText editText;

    public GroupedInputFormatWatcher(EditText editText) {
        this.editText = editText;
    }

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

    }

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

    }

    @Override
    public void afterTextChanged(Editable s) {
        String originalString = s.toString();

        // Check if we are already updating, to avoid infinite loop.
        // Also check if the string is already in a valid format.
        if (isUpdating || originalString.matches(regexp)) {
            return;
        }

        // Set flag to indicate that we are updating the Editable.
        isUpdating = true;

        // First all whitespaces must be removed. Find the index of all whitespace.
        LinkedList<Integer> spaceIndices = new LinkedList <Integer>();
        for (int index = originalString.indexOf(SPACE_CHAR); index >= 0; index = originalString.indexOf(SPACE_CHAR, index + 1)) {
            spaceIndices.offerLast(index);
        }

        // Delete the whitespace, starting from the end of the string and working towards the beginning.
        Integer spaceIndex = null;
        while (!spaceIndices.isEmpty()) {
            spaceIndex = spaceIndices.removeLast();
            s.delete(spaceIndex, spaceIndex + 1);
        }

        // Loop through the string again and add whitespaces in the correct positions
        for(int i = 0; ((i + 1) * GROUPSIZE + i) < s.length(); i++) {
            s.insert((i + 1) * GROUPSIZE + i, SPACE_STRING);
        }

        // Finally check that the cursor is not placed before a whitespace.
        // This will happen if, for example, the user deleted the digit '5' in
        // the string: "1234 567".
        // If it is, move it back one step; otherwise it will be impossible to delete
        // further numbers.
        int cursorPos = editText.getSelectionStart();
        if (cursorPos > 0 && s.charAt(cursorPos - 1) == SPACE_CHAR) {
            editText.setSelection(cursorPos - 1);
        }

        isUpdating = false;
    }
}

이 구현은 사용자가 중간 문자열을 편집하더라도 공백 문자의 올바른 배치를 보장합니다. 소프트 키보드에 표시되는 다른 문자 (예 : 대시)도 지원됩니다. 즉, 사용자가 입력 할 수 없습니다. 한 가지 개선 할 수 있습니다.이 구현에서는 문자열 중간에 공백 문자를 삭제할 수 없습니다.

public class CreditCardTextWatcher implements TextWatcher {

    public static final char SPACING_CHAR = '-'; // Using a Unicode character seems to stuff the logic up.

    @Override
    public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) { }

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

    @Override
    public void afterTextChanged(final Editable s) {
        if (s.length() > 0) {

            // Any changes we make to s in here will cause this method to be run again.  Thus we only make changes where they need to be made,
            // otherwise we'll be in an infinite loop.

            // Delete any spacing characters that are out of place.
            for (int i=s.length()-1; i>=0; --i) {
                if (s.charAt(i) == SPACING_CHAR  // There is a spacing char at this position ,
                        && (i+1 == s.length()    // And it's either the last digit in the string (bad),
                        || (i+1) % 5 != 0)) {    // Or the position is not meant to contain a spacing char?

                    s.delete(i,i+1);
                }
            }

            // Insert any spacing characters that are missing.
            for (int i=14; i>=4; i-=5) {
                if (i < s.length() && s.charAt(i) != SPACING_CHAR) {
                    s.insert(i, String.valueOf(SPACING_CHAR));
                }
            }
        }
    }
}

PasswordTransformationMethodCC 숫자를 마스킹 하는 적절한 구현 과 잘 작동합니다 .


방금 다음 구현을 수행했으며 .NET Framework의 모든 위치에 새 텍스트를 붙여넣고 입력하는 경우에도 잘 작동합니다 EditText.

요점 파일

/**
 * Text watcher for giving "#### #### #### ####" format to edit text.
 * Created by epool on 3/14/16.
 */
public class CreditCardFormattingTextWatcher implements TextWatcher {

    private static final String EMPTY_STRING = "";
    private static final String WHITE_SPACE = " ";
    private String lastSource = EMPTY_STRING;

    @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(Editable s) {
        String source = s.toString();
        if (!lastSource.equals(source)) {
            source = source.replace(WHITE_SPACE, EMPTY_STRING);
            StringBuilder stringBuilder = new StringBuilder();
            for (int i = 0; i < source.length(); i++) {
                if (i > 0 && i % 4 == 0) {
                    stringBuilder.append(WHITE_SPACE);
                }
                stringBuilder.append(source.charAt(i));
            }
            lastSource = stringBuilder.toString();
            s.replace(0, s.length(), lastSource);
        }
    }

}

용법: editText.addTextChangedListener(new CreditCardFormattingTextWatcher());


TextWatcher 가 사용하기에 올바른지 확실하지 않습니다. InputFilter를 사용해야합니다.

Android 문서에 따르면 TextWatcher를 외부 사용 예제로 사용해야합니다. 하나는 비밀번호 입력 용 [EditView] + 하나는 "weak", "strong"등을 표시하는 [TextView] 뷰입니다.

의 경우 신용 카드 형식 내가 사용하고 InputFilter를 :

public class CreditCardInputFilter implements InputFilter {
    public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
        if (dest != null & dest.toString().trim().length() > 24) return null;
        if (source.length() == 1 && (dstart == 4 || dstart == 9 || dstart == 14))
            return " " + new String(source.toString());
        return null; // keep original
    }
}

길이 필터 (Android SDK)와 결합 :

mEditCardNumber.setFilters(new InputFilter[]{
     new InputFilter.LengthFilter(24),
     new CreditCardInputFilter(),
});

이것은 숫자를 입력하고 제거 할 때 케이스를 처리합니다.

(!) 그러나 이것은 전체 문자열의 복사 / 붙여 넣기의 경우를 처리하지 않습니다. 이것은 다른 InputFilter 클래스에서 수행되어야합니다.

도움이 되길 바랍니다!


프로젝트를 보세요 . Android 양식 편집 텍스트는 편집 텍스트에 데이터 유효성 검사 기능을 제공하는 EditText의 확장입니다.


많은 검색을했지만 내 요구에 맞는 만족스러운 답을 얻지 못해 결국 내 자신의 기능을 작성하게되었습니다.

다음은 입력중인 카드 유형에 따라 입력 된 신용 카드 세부 정보의 형식을 지정하는 예입니다. 현재 포맷을 위해 Visa, MasterCard 및 American Express를 처리합니다.

    editTxtCardNumber.addTextChangedListener(new TextWatcher() {

        private boolean spaceDeleted;

        @Override
        public void onTextChanged(CharSequence s, int arg1, int arg2,
                int arg3) {

        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            CharSequence charDeleted = s.subSequence(start, start + count);
            spaceDeleted = " ".equals(charDeleted.toString());
        }

        @Override
        public void afterTextChanged(Editable editable) {

            if(editTxtCardNumber.getText().length() > 0 && editTxtCardNumber.getText().charAt(0) == '3') {
                editTxtCardNumber.setFilters(new InputFilter[] { new InputFilter.LengthFilter(Constants.MAX_LENGTH_CARD_NUMBER_AMEX) });

                editTxtCardNumber.removeTextChangedListener(this);
                int cursorPosition = editTxtCardNumber.getSelectionStart();
                String withSpaces = formatTextAmEx(editable);
                editTxtCardNumber.setText(withSpaces);
                editTxtCardNumber.setSelection(cursorPosition + (withSpaces.length() - editable.length()));

                if (spaceDeleted) {
                    editTxtCardNumber.setSelection(editTxtCardNumber.getSelectionStart() - 1);
                    spaceDeleted = false;
                }

                editTxtCardNumber.addTextChangedListener(this);
            } else if(editTxtCardNumber.getText().length() > 0 
                    && (editTxtCardNumber.getText().charAt(0) == '4' || editTxtCardNumber.getText().charAt(0) == '5')) {
                editTxtCardNumber.setFilters(new InputFilter[] { new InputFilter.LengthFilter(Constants.MAX_LENGTH_CARD_NUMBER_VISA_MASTERCARD) });

                editTxtCardNumber.removeTextChangedListener(this);
                int cursorPosition = editTxtCardNumber.getSelectionStart();
                String withSpaces = formatTextVisaMasterCard(editable);
                editTxtCardNumber.setText(withSpaces);
                editTxtCardNumber.setSelection(cursorPosition + (withSpaces.length() - editable.length()));

                if (spaceDeleted) {
                    editTxtCardNumber.setSelection(editTxtCardNumber.getSelectionStart() - 1);
                    spaceDeleted = false;
                }

                editTxtCardNumber.addTextChangedListener(this);
            } else {
                editTxtCardNumber.setFilters(new InputFilter[] { new InputFilter.LengthFilter(Constants.MAX_LENGTH_CARD_NUMBER_VISA_MASTERCARD) });

                editTxtCardNumber.removeTextChangedListener(this);
                int cursorPosition = editTxtCardNumber.getSelectionStart();
                String withSpaces = formatTextVisaMasterCard(editable);
                editTxtCardNumber.setText(withSpaces);
                editTxtCardNumber.setSelection(cursorPosition + (withSpaces.length() - editable.length()));

                if (spaceDeleted) {
                    editTxtCardNumber.setSelection(editTxtCardNumber.getSelectionStart() - 1);
                    spaceDeleted = false;
                }

                editTxtCardNumber.addTextChangedListener(this);
            }
        }
    });

    private String formatTextVisaMasterCard(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();
    }

    private String formatTextAmEx(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 > 0 && ((count == 4) || (count == 10))) {
                    formatted.append(" ");
                }
                formatted.append(text.charAt(i));
                ++count;
            }
        }
        return formatted.toString();
    }

서식 공간 외에도 카드 번호가 최대 한도를 초과하지 않는지 확인하고 사용자가 최대 한도에 도달하면 글꼴을 변경하여 모든 자릿수를 입력했다는 알림을받습니다. 위에서 언급 한 작업을 수행하는 기능은 다음과 같습니다.

public void checkCardNoEnteredCorrectly() {
if(editTxtCardNumber.getText().length() > 0 && editTxtCardNumber.getText().charAt(0) == '3') {
    if(editTxtCardNumber.getText().length() == Constants.MAX_LENGTH_CARD_NUMBER_AMEX) {
        editTxtCardNumber.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.amex), null, null, null);
    } else {
        editTxtCardNumber.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.amex), null, null, null);
    }
} else if(editTxtCardNumber.getText().length() > 0 && editTxtCardNumber.getText().charAt(0) == '4') {
    if(editTxtCardNumber.getText().length() == Constants.MAX_LENGTH_CARD_NUMBER_VISA_MASTERCARD) {
        editTxtCardNumber.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.visa), null, null, null);
    } else {
        editTxtCardNumber.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.visa), null, null, null);
    }
} else if(editTxtCardNumber.getText().length() > 0 && editTxtCardNumber.getText().charAt(0) == '5') {
    if(editTxtCardNumber.getText().length() == Constants.MAX_LENGTH_CARD_NUMBER_VISA_MASTERCARD) {
        editTxtCardNumber.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.master_card), null, null, null);
    } else {
        editTxtCardNumber.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.master_card), null, null, null);
    }
} else {
    editTxtCardNumber.setCompoundDrawablesWithIntrinsicBounds(getResources().getDrawable(R.drawable.credit_card_number), null, null, null);
}

}

참고 : Constants.java의 선언은 다음과 같습니다.

public static final int MAX_LENGTH_CARD_NUMBER_VISA_MASTERCARD = 19;
public static final int MAX_LENGTH_CARD_NUMBER_AMEX = 17;

도움이 되었기를 바랍니다.


내 솔루션은 중간 텍스트 작업이나 복사-붙여 넣기 작업이 무엇이든 잘 작동 할 수 있다고 생각합니다.

아래 코드를 참조하십시오.

  class BankNumberTextWatcher implements TextWatcher {
      private int previousCodeLen = 0;

      @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(Editable s) {
          if (s.length() > 0) {
              String numbersOnly = s.toString().replaceAll("[^0-9]", "");
              // current code pattern miss-match, then handle cursor position and format the code
              handleEditInput(numbersOnly);
          } else {
              previousCodeLen = 0;
          }
      }

      /**
       * Handle EditText input process for credit card including insert, delete during middle position,
       * end position or copy-paste controller
       *
       * @param numbersOnly the pure number without non-digital characters
       */
      private void handleEditInput(final String numbersOnly) {
          String code = formatNumbersAsCode(numbersOnly);
          int cursorStart = etBankCardNumber.getSelectionStart();
          etBankCardNumber.removeTextChangedListener(this);
          etBankCardNumber.setText(code);
          int codeLen = code.length();
          if (cursorStart != codeLen) {
             // middle-string operation
             if (cursorStart > 0 && cursorStart % 5 == 0) {
                if (codeLen > previousCodeLen) {
                    // insert, move cursor to next
                    cursorStart++;
                } else if (codeLen < previousCodeLen) {
                    // delete, move cursor to previous
                    cursorStart--;
                }
             }
             etBankCardNumber.setSelection(cursorStart);
          } else {
             // end-string operation
             etBankCardNumber.setSelection(codeLen);
          }
          etBankCardNumber.addTextChangedListener(this);
          previousCodeLen = codeLen;
      }

      /**
       * formats credit code like 1234 1234 5123 1234
       *
       * @param s
       * @return
       */
       public String formatNumbersAsCode(CharSequence s) {
          if (TextUtils.isEmpty(s)) {
            return "";
          }
          int len = s.length();
          StringBuilder tmp = new StringBuilder();
          for (int i = 0; i < len; ++i) {
              tmp.append(s.charAt(i));
              if ((i + 1) % 4 == 0 && (i + 1) != len) {
                  tmp.append(" ");
              }
          }
          return tmp.toString();
        }
  }

레이아웃 파일에서 다른 문자를 피하기 위해 EditText의 inputType을 숫자로 만듭니다.

도움이 되길 바랍니다.


이미 알아 냈을 수도 있지만 여기에 내가 한 일이 있습니다. 내가 재정의해야하는 유일한 방법은 AfterTextChanged였습니다.

신용 카드의 형식이 이미 유효한지 확인하고 무한 재귀를 방지하기 위해 기본 케이스

양식이 유효하지 않으면 모든 공백을 제거하고 다른 문자열로 복사하여 적절한 곳에 공백을 삽입하십시오.

그런 다음 편집 가능 항목을 새 문자열로 바꾸십시오.

특정 단계에 대한 코드가 필요하면 언제든지 문의하십시오.

그리고 Preethi, 공백을 삭제할 수없는 이유는 onTextChanged 콜백에서 텍스트를 변경할 수 없기 때문입니다. 개발자 사이트에서 :

public abstract void onTextChanged (CharSequence s, int start, int before, int count) API 레벨 1에 추가됨

이 메서드는 s 내에서 시작에서 시작하는 카운트 문자가 이전에 길이가 있었던 이전 텍스트를 방금 대체했음을 알리기 위해 호출됩니다. 이 콜백에서에 변경을 시도하는 것은 오류입니다.


int          keyDel;
String       a;
String       a0;
int          isAppent = 0;
final String ch       = " ";

private void initListner() {


    txtCreditNumber.addTextChangedListener(new TextWatcher() {

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

            boolean flag = true;
            if (s.length() > 19) {
                txtCreditNumber.setText(a0);
                txtCreditNumber.setSelection(txtCreditNumber.getText().length());
                return;
            }
            String eachBlock[] = s.toString().split(ch);
            for(int i = 0; i < eachBlock.length; i++) {
                if (eachBlock[i].length() > 4) {
                    flag = false;
                }
            }
            if (a0.length() > s.toString().length()) {
                keyDel = 1;
            }
            if (flag) {
                if (keyDel == 0) {

                    if (((txtCreditNumber.getText().length() + 1) % 5) == 0) {

                        if (s.toString().split(ch).length <= 3) {
                            isAppent = 1;
                            txtCreditNumber.setText(s + ch);
                            isAppent = 0;
                            txtCreditNumber.setSelection(txtCreditNumber.getText().length());
                            a = txtCreditNumber.getText().toString();
                            return;
                        }
                    }
                    if (isAppent == 0) {
                        String str = s.toString();
                        if (str.lastIndexOf(ch) == str.length() - 1) {
                            str = str.substring(0, str.lastIndexOf(ch));
                            keyDel = 1;
                            txtCreditNumber.setText(str);
                            keyDel = 0;
                            txtCreditNumber.setSelection(txtCreditNumber.getText().length());
                            a = txtCreditNumber.getText().toString();
                            return;
                        }
                    }

                }
                else {
                    String str = s.toString();
                    if (str.length() > 0 && str.lastIndexOf(ch) == str.length() - 1) {
                        str = str.substring(0, str.lastIndexOf(ch));
                        keyDel = 1;
                        txtCreditNumber.setText(str);
                        keyDel = 0;
                        txtCreditNumber.setSelection(txtCreditNumber.getText().length());
                        a = txtCreditNumber.getText().toString();
                        return;
                    }
                    else {
                        a = txtCreditNumber.getText().toString();
                        keyDel = 0;
                    }
                }

            }
            else {
                String str = s.toString();
                str = str.substring(0, str.length() - 1) + ch + str.substring(str.length() - 1, str.length());

                a = str;
                txtCreditNumber.setText(a);
                txtCreditNumber.setSelection(txtCreditNumber.getText().length());
            }

        }

        @Override
        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
            // TODO Auto-generated method stub
            a0 = s.toString();
        }

        @Override
        public void afterTextChanged(Editable s) {
        }
    });
}

다음은 모든 기능을 적절히 사용하여 결정을 내리는 예입니다. 코드는 조금 더 길 수 있지만 주로 주어진 값 (start, before, count ...)을 사용하기 때문에 더 빠를 것입니다. 이 예에서는 사용자가 백 스페이스를 사용할 때 4 자리마다 "-"를 추가하고 삭제합니다. 또한 커서가 끝에 있는지 확인하십시오.

public class TextWatcherImplement implements TextWatcher {

private EditText creditCard;
private String beforeText, currentText;
private boolean noAction, addStroke, dontAddChar, deleteStroke;

public TextWatcherImplement(EditText creditCard) {
    // TODO Auto-generated constructor stub
    this.creditCard = creditCard;
    noAction = false;
    addStroke = false;
    dontAddChar = false;
    deleteStroke = false;
}

/* here I save the previous string if the max character had achieved */
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    // TODO Auto-generated method stub
    Log.i("TextWatcherImplement", "beforeTextChanged start==" + String.valueOf(start) + " count==" + String.valueOf(count) + " after==" + String.valueOf(after));
    if (start >= 19)
        beforeText = s.toString();
}


/* here I check were we add a character, or delete one. 
if we add character and it is time to add a stroke, then I flag it -> addStroke 
if we delete a character and it time to delete a stroke, I flag it -> deleteStroke
if we are in max character for the credit card, don't add char -> dontAddChar 
*/
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
    // TODO Auto-generated method stub
    Log.i("TextWatcherImplement", "onTextChanged start==" + String.valueOf(start) + " before==" + String.valueOf(before) + " count==" + String.valueOf(count) + " noAction ==" + String.valueOf(noAction));
    if ( (before < count) && !noAction ) {
        if ( (start == 3) || (start == 8) || (start == 13) ) {
            currentText = s.toString();
            addStroke = true;
        } else if (start >= 19) {
            currentText = s.toString();
            dontAddChar = true;
        }
    } else {
        if ( (start == 4) ||  (start == 9) ||  (start == 14)  ) { //(start == 5) || (start == 10) || (start == 15)
            currentText = s.toString();
            deleteStroke = true;
        }
    }
}

/* noAction flag is when we change the text, the interface is being called again.
the NoAction flag will prevent any action, and prevent a ongoing loop */

@Override
public void afterTextChanged(Editable stext) {
    // TODO Auto-generated method stub
    if (addStroke) {
        Log.i("TextWatcherImplement", "afterTextChanged String == " + stext + " beforeText == " + beforeText + " currentText == " + currentText);
        noAction = true;
        addStroke = false;
        creditCard.setText(currentText + "-");
    } else if (dontAddChar) {
        dontAddChar = false;
        noAction = true;
        creditCard.setText(beforeText);
    } else if (deleteStroke) {
        deleteStroke = false;
        noAction = true;
        currentText = currentText.substring(0, currentText.length() - 1);
        creditCard.setText(currentText);
    } else {
        noAction = false;
        creditCard.setSelection(creditCard.getText().length()); // set cursor at the end of the line.
    }
}

}


여기 내 해결책이 있습니다. 내 의견은 Android 개발자가 무슨 일이 일어나고 있는지 이해하기에 충분한 정보로 충분해야하지만 질문이 있으시면 언제든지 질문하시면 제가 아는 한 최선을 다해 답변 해 드리겠습니다.

private KeyEvent keyEvent;

final TextWatcher cardNumberWatcher = new TextWatcher() {
        @Override
        public void beforeTextChanged(CharSequence charSequence, int start, int before, int count) {
            // NOT USING
        }

        @Override
        public void onTextChanged(CharSequence charSequence, int start, int before, int count) {
            // NOT USING
        }

        @Override
        public void afterTextChanged(Editable editable) {
            String cardNumbersOnly = editable.toString().replace("-", "");

            /**
            * @PARAM keyEvent
            * This gets called upon deleting a character so you must keep a 
            * flag to ensures this gets skipped during character deletion
            */
            if (cardNumbersOnly.length() >= 4 && keyEvent == null) {
                formatCreditCardTextAndImage(this);
            }

            keyEvent = null;
        }
    };

    cardNumberEditText.addTextChangedListener(cardNumberWatcher);

    /**
    * @LISTENER
    * Must keep track of when the backspace event has been fired to ensure    
    * that the delimiter character and the character before it is deleted 
    * consecutively to avoid the user from having to press backspace twice 
    */
    cardNumberEditText.setOnKeyListener(new View.OnKeyListener() {
        @Override
        public boolean onKey(View v, int keyCode, KeyEvent event) {
            if (event.getAction() != KeyEvent.ACTION_UP) {
                // Hold reference of key event for checking within the text watcher
                keyEvent = event;
                String cardNumberString = cardNumberEditText.getText().toString();

                if (keyCode == event.KEYCODE_DEL) {
                    if (cardNumberString.substring(cardNumberString.length() - 1).equals("-")) {
                        // Remove listener to avoid infinite looping
                        cardNumberEditText.removeTextChangedListener(cardNumberWatcher);
                        // Remove hyphen and character before it
                        cardNumberEditText.setText(cardNumberString.substring(0, cardNumberString.length() - 1));
                        // Set the cursor back to the end of the text
                        cardNumberEditText.setSelection(cardNumberEditText.getText().length());
                        // Add the listener back
                        cardNumberEditText.addTextChangedListener(cardNumberWatcher);
                    }
                    else if (cardNumberString.length() < 2) {
                        cardNumberBrandImageView.setImageDrawable(null);
                        cardNumberBrandImageView.setVisibility(View.INVISIBLE);
                    }
                }
            }
            return false;
        }
    });
}

private void formatCreditCardTextAndImage (TextWatcher textWatcher) {
    // Remove to avoid infinite looping
    cardNumberEditText.removeTextChangedListener(textWatcher);

    String cardNumberString = cardNumberEditText.getText().toString();

    /**
    * @CONDITION
    * Append delimiter after every fourth character excluding the 16th
    */
    if ((cardNumberString.length() + 1) % 5 == 0 && !cardNumberString.substring(cardNumberString.length() - 1).equals("-")) {
            cardNumberEditText.setText(cardNumberString + "-");
    }

    // Set the cursor back to the end of the text
    cardNumberEditText.setSelection(cardNumberEditText.getText().length());
    cardNumberEditText.addTextChangedListener(textWatcher);

    /**
    * @CardBrand
    * Is an enum utility class that checks the card numbers 
    * against regular expressions to determine the brand and updates the UI
    */
    if (cardNumberString.length() == 2) {
        switch (CardBrand.detect(cardNumberEditText.getText().toString())) {
            case VISA:
                cardNumberBrandImageView.setImageResource(R.drawable.visa);
                cardNumberBrandImageView.setVisibility(View.VISIBLE);
                card.setBrand(Brand.Visa);
                break;
            case MASTERCARD:
                cardNumberBrandImageView.setImageResource(R.drawable.mastercard);
                cardNumberBrandImageView.setVisibility(View.VISIBLE);
                card.setBrand(Brand.MasterCard);
                break;
            case DISCOVER:
                cardNumberBrandImageView.setImageResource(R.drawable.discover);
                cardNumberBrandImageView.setVisibility(View.VISIBLE);
                card.setBrand(Brand.Discover);
                break;
            case AMERICAN_EXPRESS:
                cardNumberBrandImageView.setImageResource(R.drawable.americanexpress);
                cardNumberBrandImageView.setVisibility(View.VISIBLE);
                card.setBrand(Brand.AmericanExpress);
                break;
            case UNKNOWN:
                cardNumberBrandImageView.setImageDrawable(null);
                cardNumberBrandImageView.setVisibility(View.INVISIBLE);
                card.setBrand(null);
                break;
        }
    }
}

다음은 TextWatcher클래스를 사용하여 간단하고 쉽게 사용자 정의 할 수있는 솔루션 입니다. 방법을 EditText사용하여 귀하에게 할당 될 수 있습니다 addTextChangedListener().

new TextWatcher() {
    /** Formats the Field to display user-friendly separation of the input values. */
    @Override public final void afterTextChanged(final Editable pEditable) {
        // Declare the separator.
        final char lSeparator      = '-';
        // Declare the length of separated text. i.e. (XXXX-XXXX-XXXX)
        final int  lSeparationSize = 4;
        // Declare the count; tracks the number of allowed characters in a row.
              int lCount          = 0;
        // Iterate the Characters.
        for(int i = 0; i < pEditable.length(); i++) {
            // Fetch the current character.
            final char c              = pEditable.charAt(i);
            // Is it a usual character. Here, we permit alphanumerics only.
            final boolean lIsExpected = (Character.isDigit(c) || Character.isLetter(c)) && (c != lSeparator);
            // Is the character expected?
            if(lIsExpected) {
                // Increase the count.
                lCount++;
            }
            else {
                // Is it a separator?
                if(c == lSeparator) {
                    // Reset the count.
                    lCount = 0;
                    // Continue the iteration.
                    continue;
                }
            }
            // Has the count been exceeded? Is there more text coming?
            if(lCount >= (lSeparationSize + 1) && (i < pEditable.length())) {
                // Reset the count.
                lCount = 0;
                // Insert the separator.
                pEditable.insert(i, Character.toString(lSeparator));
                // Increase the iteration count.
                i++;
            }
        }
    }
    /** Unused overrides. */
    @Override public final void beforeTextChanged(final CharSequence pCharSequence, final int pStart, final int pCount, final int pAfter) { }
    @Override public final void onTextChanged(final CharSequence pCharSequence, final int pStart, final int pBefore, final int pCount) { }
}

또는 여기에 epool의 구현을 기반으로 한 훨씬 더 깨끗한 구현 있습니다.

public final class TextGroupFormattingListener implements TextWatcher {

    /* Member Variables. */
    private final int    mGroupLength;
    private final String mSeparator;
    private       String mSource;

    /** Constructor. */
    public TextGroupFormattingListener(final String pSeparator, final int pGroupLength) {
        // Initialize Member Variables.
        this.mSeparator   = pSeparator;
        this.mGroupLength = pGroupLength;
        this.mSource      = "";
    }

    /** Formats the Field to display user-friendly separation of the input values. */
    @Override public final void afterTextChanged(final Editable pEditable) {
        // Fetch the Source.
        String lSource = pEditable.toString();
        // Has the text changed?
        if (!this.getSource().equals(lSource)) {
            // Remove all of the existing Separators.
            lSource = lSource.replace(this.getSeparator(), "");
            // Allocate a StringBuilder.
            StringBuilder lStringBuilder = new StringBuilder();
            // Iterate across the Source String, which contains the raw user input.
            for(int i = 0; i < lSource.length(); i++) {
                // Have we exceeded the GroupLength?
                if(i > 0 && i % this.getGroupLength() == 0) {
                    // Append the separator.
                    lStringBuilder.append(this.getSeparator());
                }
                // Append the user's character data.
                lStringBuilder.append(lSource.charAt(i));
            }
            // Track changes to the Source.
            this.setSource(lStringBuilder.toString());
            // Replace the contents of the Editable with this new String.
            pEditable.replace(0, pEditable.length(), this.getSource());
        }
    }

    /** Unused overrides. */
    @Override public final void beforeTextChanged(final CharSequence pCharSequence, final int pStart, final int pCount, final int pAfter) { }
    @Override public final void onTextChanged(final CharSequence pCharSequence, final int pStart, final int pBefore, final int pCount)    { }

    public final int getGroupLength() {
        return this.mGroupLength;
    }

    public final String getSeparator() {
        return this.mSeparator;
    }

    private final void setSource(final String pSource) {
        this.mSource = pSource;
    }

    private final String getSource() {
        return this.mSource;
    }

}

위의 답변 중 어느 것도 나에게 완벽하지 않습니다. 시작 문자열 / 종료 문자열 / 중간 문자열 문제를 해결하는 하나를 만들었습니다. 복사 및 붙여 넣기도 잘 작동합니다. 이것은 Mastercard, Visa 및 Amex를 지원합니다. 구분 기호를 변경할 수 있습니다. 지불 방법 유형이 필요하지 않으면 제거하십시오. 그래도 Kotlin입니다. 아이디어는 간단합니다. 텍스트가 변경 될 때마다 모든 구분 기호를 제거하고 형식에 따라 다시 추가했습니다. 시작 문자열 / 중간 문자열 문제를 해결합니다. 그런 다음 유일한 문제는 구분 기호를 추가 한 후 올바른 텍스트 위치를 찾아야한다는 것입니다.

fun addCreditCardNumberTxtWatcher(et: EditText, separator: Char, paymentMethodType: PaymentMethodType): TextWatcher {
    val tw = object : TextWatcher {
        var mBlock = false
        override fun afterTextChanged(s: Editable) {
        }
        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
              Logger.d("_debug", "s: $s, start: $start, count: $count, after $after")
        }
        override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
            if (mBlock)
                return
            var lastPos = et.selectionStart
            val oldStr = et.text.toString().replace(separator.toString(), "", false)
            var newFormattedStr = ""
            if (before > 0) {
                if (lastPos > 0 && et.text.toString()[lastPos - 1] == separator) lastPos--
            }
            Logger.d("_debug", "lastPos: $lastPos, s: $s, start: $start, before: $before, count $count")
            mBlock = true
            oldStr.forEachIndexed { i, c ->
                when (paymentMethodType) {
                    PaymentMethodType.MASTERCARD, PaymentMethodType.VISA -> {
                        if (i > 0 && i % 4 == 0) {
                            newFormattedStr += separator
                        }
                    }
                    PaymentMethodType.AMERICAN_EXPRESS -> {
                        if (i == 4 || i == 10 || i == 15) {
                            newFormattedStr += separator
                        }
                    }
                }
                newFormattedStr += c
            }
            et.setText(newFormattedStr)
            if (before == 0) {
                if (et.text.toString()[lastPos - 1] == separator) lastPos++
            }
            et.setSelection(lastPos)
            mBlock = false
        }
    }
    et.addTextChangedListener(tw)
    return tw
}

이 솔루션은 IBAN을 위해 구현되었지만 원칙은 동일합니다. 위 답변의 모든 주요 문제를 수정하려고 노력했습니다. 오류가 발견되면 언제든지 말씀해주세요. 감사합니다.

EditText를 설정하고 사용할 수있는 문자를 제한하십시오.

private void setEditTextIBAN(View view) {
    editTextIBAN = (EditText) view.findViewById(R.id.client_iban);
    editTextIBAN.setKeyListener(
            DigitsKeyListener.getInstance("ABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 "));
    editTextIBAN.addTextChangedListener(new IBANTextWatcher());
}

이것은 TextWatcher입니다.

private class IBANTextWatcher implements TextWatcher {

    // means divider position is every 5th symbol
    private static final int DIVIDER_MODULO = 5;
    private static final int GROUP_SIZE = DIVIDER_MODULO - 1;
    private static final char DIVIDER = ' ';
    private static final String STRING_DIVIDER = " ";
    private String previousText = "";

    private int deleteLength;
    private int insertLength;
    private int start;

    private String regexIBAN = "(\\w{" + GROUP_SIZE + "}" + DIVIDER +
            ")*\\w{1," + GROUP_SIZE + "}";
    private Pattern patternIBAN = Pattern.compile(regexIBAN);

    @Override
    public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {
        this.previousText = s.toString();
        this.deleteLength = count;
        this.insertLength = after;
        this.start = start;
    }

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

    }

    @Override
    public void afterTextChanged(final Editable s) {
        String originalString = s.toString();

        if (!previousText.equals(originalString) &&
                !isInputCorrect(originalString)) {
            String newString = previousText.substring(0, start);
            int cursor = start;

            if (deleteLength > 0 && s.length() > 0 &&
                    (previousText.charAt(start) == DIVIDER ||
                            start == s.length())) {
                newString = previousText.substring(0, start - 1);
                --cursor;
            }

            if (insertLength > 0) {
                newString += originalString.substring(start, start + insertLength);
                newString = buildCorrectInput(newString);
                cursor = newString.length();
            }

            newString += previousText.substring(start + deleteLength);
            s.replace(0, s.length(), buildCorrectInput(newString));

            editTextIBAN.setSelection(cursor);
        }
    }

    /**
     * Check if String has the white spaces in the correct positions, meaning
     * if we have the String "123456789" and there should exist a white space
     * every 4 characters then the correct String should be "1234 5678 9".
     *
     * @param s String to be evaluated
     * @return true if string s is written correctly
     */
    private boolean isInputCorrect(String s) {
        Matcher matcherDot = patternIBAN.matcher(s);
        return matcherDot.matches();
    }

    /**
     * Puts the white spaces in the correct positions,
     * see the example in {@link IBANTextWatcher#isInputCorrect(String)}
     * to understand the correct positions.
     *
     * @param s String to be corrected.
     * @return String corrected.
     */
    private String buildCorrectInput(String s) {
        StringBuilder sbs = new StringBuilder(
                s.replaceAll(STRING_DIVIDER, ""));

        // Insert the divider in the correct positions
        for (int i = GROUP_SIZE; i < sbs.length(); i += DIVIDER_MODULO) {
            sbs.insert(i, DIVIDER);
        }

        return sbs.toString();
    }
}

레이아웃에서 :

    <android.support.design.widget.TextInputEditText
        android:id="@+id/et_credit_card_number"
        android:digits=" 1234567890"
        android:inputType="number"
        android:maxLength="19"/>

여기에서 TextWachter16 자리 신용 카드의 4 자리마다 공백을 설정합니다.

class CreditCardFormatWatcher : TextWatcherAdapter() {

    override fun afterTextChanged(s: Editable?) {
        if (s == null || s.isEmpty()) return

        s.forEachIndexed { index, c ->
            val spaceIndex = index == 4 || index == 9 || index == 14
            when {
                !spaceIndex && !c.isDigit()     -> s.delete(index, index + 1)
                spaceIndex && !c.isWhitespace() -> s.insert(index, " ")
            }
        }

        if (s.last().isWhitespace())
            s.delete(s.length - 1, s.length)
    }

}

 private class TextWatcherIBAN implements 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(Editable s) {
            textInputEditText.removeTextChangedListener(this);
            formatIBANEditText(textInputEditText);
            textInputEditText.addTextChangedListener(this);

        }
    }


public void formatIBANEditText(TextInputEditText editText) {
    String decimalAmount = editText.getText().toString();
    int selection = editText.getSelectionEnd() == decimalAmount.length() ? -1 : editText.getSelectionEnd();
    decimalAmount = formatIBAN(decimalAmount);
    editText.setText(decimalAmount);

    if (selection != -1) {
        editText.setSelection(selection);
    } else {
        editText.setSelection(decimalAmount.length());
    }

}

public String formatIBAN(String text) {
    return formatterIBAN(new StringBuilder(text));
}

private String formatterIBAN(StringBuilder text) {
    int group = text.toString().length() / 5;
    int spaceCount = getSpaceCount(text);
    if (spaceCount < group) {
        return formatterIBAN(text.insert(4 + 5 * spaceCount, space));
    } else {
        return text.toString();
    }
}

private int getSpaceCount(StringBuilder text) {
    int spaceCount = 0;
    for (int index = 0; index < text.length(); index++) {
        if (text.charAt(index) == space.charAt(0)) {
            spaceCount++;
        }
    }
    return spaceCount;
}


textInputEditText.addTextChangedListener(new TextWatcherIBAN());

class XYZ : TextWatcher {

private val formatSymbols = DecimalFormatSymbols(Locale.getDefault())

private lateinit var formatter: DecimalFormat

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
    .
    .
    formatSymbols.groupingSeparator = ' '
    formatter = DecimalFormat("####,####", formatSymbols)
    .
    .
}

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    editText.addTextChangedListener(this)
}

override fun afterTextChanged(s: Editable?) {
    if (editText.error != null) {
        editText.error = null
    }
    editText.removeTextChangedListener(this)
    try {
        var originalString = s.toString()
        if (originalString.contains(" ")) {
            originalString = originalString.replace(" ", "", true)
        }
        val longVal: Long? = originalString.toLong()
        val formattedString = formatter.format(longVal)
        editText.setText(formattedString)
        editText.setSelection(editText.text.length)
    } catch (error: NumberFormatException) {
        // Print Error Or Do Whatever you want.
    }
    editText.addTextChangedListener(this)
}

}

이것은 내 구현 기지 이고르 Tyulkanov의 생각 , 그것은 커서 위치의 문제를 해결 작은 개선이있다

class CardNumbersInputWatcher(private val editText: EditText) : TextWatcher {
  companion object {
    private const val TOTAL_SYMBOLS = 19
    private const val DIVIDER_DISTANCE = 4
    private const val DIVIDER = ' '
  }

  override fun afterTextChanged(s: Editable) {
    if (!isInputCorrect(s, TOTAL_SYMBOLS, DIVIDER_DISTANCE, DIVIDER)) {
      val beforeCurPos = editText.selectionStart
      val beforeLength = s.length
      s.replace(0, s.length, buildCorrectString(s, TOTAL_SYMBOLS, DIVIDER_DISTANCE, DIVIDER))
      if (beforeLength > TOTAL_SYMBOLS && beforeCurPos <= s.length && editText.selectionStart < beforeCurPos) {
        editText.setSelection(beforeCurPos)
      }
    }
  }

  override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit
  override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = Unit
}

private fun isInputCorrect(s: Editable, totalSymbols: Int, dividerDistance: Int, divider: Char): Boolean {
  if (s.length > totalSymbols) {
    return false
  }
  return s.withIndex().all { (index, c) ->
    if (index != 0 && ((index + 1) % (dividerDistance + 1) == 0)) {
      // it should be divider
      c == divider
    } else {
      c.isDigit()
    }
  }
}

private fun buildCorrectString(s: Editable, totalSymbols: Int, dividerDistance: Int, divider: Char): String {
  return buildString {
    for (c in s) {
      if (length >= totalSymbols) break
      if (!c.isDigit()) continue
      if (length > 0 && ((length + 1) % (dividerDistance + 1)) == 0) append(divider)
      append(c)
    }
  }
}

참조 URL : https://stackoverflow.com/questions/11790102/format-credit-card-in-edit-text-in-android

반응형