Format credit card in edit text in android

后端 未结 29 1961
耶瑟儿~
耶瑟儿~ 2020-11-30 19:18

How to make EditText accept input in format:

4digit 4digit 4digit 4digit 

I tried Custom format edit text input android to acc

相关标签:
29条回答
  • 2020-11-30 19:47

    If anyone still looking for answer,

    Try the format-edit-text library for auto-formatting text in one line of code. This library uses dash(es) to define the format of the input.

    editText.setFormat("any (dash) format");
    

    How to use

    add format-edit-text library dependency in app/build.gradle

    implementation 'com.androidwidgets:formatedittext:0.2.0'
    

    Add FormatEditText view in activity_main.xml

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity"
        android:focusableInTouchMode="true"
        android:focusable="true">
    
        <com.androidwidgets.formatedittext.widgets.FormatEditText
            android:id="@+id/edit_text_1"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginStart="16dp"
            android:layout_marginLeft="16dp"
            android:layout_marginTop="16dp"
            android:layout_marginEnd="16dp"
            android:layout_marginRight="16dp"
            android:imeOptions="actionSend"
            android:inputType="number"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.0"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    

    Set credit-card format to FormatEditText view in MainActivity.java

    public class MainActivity extends AppCompatActivity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            final FormatEditText editText1 = findViewById(R.id.edit_text_1);
            editText1.setFormat("---- ---- ---- ----");
        }
    }
    

    This will produce the below output

    PS: Make sure parameter inputType is added to the FormatEditText view in the layout file.

    android:inputType="number"
    
    0 讨论(0)
  • 2020-11-30 19:48

    After searching a lot and not getting any satisfactory answer to meet my needs, I ended up writing my own function.

    Here is an example to format entered credit card details based on the type of card being entered. Currently it takes care of Visa, MasterCard and American Express for the purpose of formatting.

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

    Other than formatting spaces, I also applied checks to make sure that card number doesn't exceed their maximum limit and user gets notified that he has entered all the digits by performing a change in font when the maximum limit is reached. Here is the function to perform the above mentioned operation.

    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);
    }
    

    }

    Note: The declarations made in Constants.java is as follows:

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

    Hope it helps!

    0 讨论(0)
  • 2020-11-30 19:48

    Here is an example that use all the function appropriately to make a decision. The code might be a bit longer, but it will be faster as it mainly use the function given values (start, before, count ...). This example add "-" every 4 digits, and delete them as well, when user use backspace. as well, make sure the cursor will be at the end.

    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.
        }
    }
    

    }

    0 讨论(0)
  • 2020-11-30 19:49

    In your layout:

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

    Here the TextWachter which sets a space on every 4 digits in a 16 number credit card.

    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)
        }
    
    }
    
    0 讨论(0)
  • 2020-11-30 19:49
     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());
    
    0 讨论(0)
  • 2020-11-30 19:49

    This is my implementation base on Igor Tyulkanov's idea, it has a small improvement that fix the cursor position problem

    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)
        }
      }
    }
    
    
    0 讨论(0)
提交回复
热议问题