76

EditTextフォーマットで入力を受け入れる方法:

4digit 4digit 4digit 4digit 

クレジットカード番号を受け入れるためにカスタムフォーマット編集テキスト入力アンドロイドを試しましたが、残念ながらスペースを削除できませんでした。スペースがあるときはいつでも削除できませんでした。問題を見つけるのを手伝ってください。

4

28 に答える 28

98

'OK'である複数の答えを見つけた後。から独立して正しく動作するように設計された、より優れた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"ます。これにより他の文字が有効になりますが、カスタムinputfilterを使用するだけで、問題が解決します。

于 2014-02-10T14:28:14.407 に答える
85

デモ-これがどのように機能するか

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

これは、開始文字列/終了文字列/中間文字列の編集で完全に機能し、貼り付けも完全に機能します。

于 2015-12-11T09:03:46.533 に答える
30

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;
            }
        }
    });
于 2014-10-23T08:34:50.453 に答える
22

これは、正規表現を使用したよりクリーンなソリューションです。正規表現は非効率的ですが、この場合、キーを押すたびに処理が行われる場合でも、最大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);
        }

    }

});
于 2015-10-12T02:06:45.443 に答える
13

これが私がクレジットカード番号に使用するクラスです。以下の使用例。

ここに画像の説明を入力してください

FormattedNumberEditText.kt

import android.content.Context
import android.text.Editable
import android.text.InputType
import android.text.TextWatcher
import android.text.method.DigitsKeyListener
import android.util.AttributeSet
import android.widget.EditText

open class FormattedNumberEditText : AppCompatEditText {

    var prefix = ""
        private set

    var groupSeparator = ' '
        private set

    var numberOfGroups = 4
        private set

    var groupLength = 4
        private set

    var inputLength = numberOfGroups * (groupLength + 1) - 1
        private set

    private val digitsKeyListener = DigitsKeyListener.getInstance("0123456789")

    private lateinit var separatorAndDigitsKeyListener: DigitsKeyListener

    private var initCompleted = false

    constructor(context: Context) : super(context) {
        init(null)
    }

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
        init(attrs)
    }

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
        init(attrs)
    }

    private fun init(attrs: AttributeSet?) {
        if (attrs != null) {
            val a = context.theme.obtainStyledAttributes(attrs, R.styleable.FormattedNumberEditText, 0, 0)
            prefix = a.getString(R.styleable.FormattedNumberEditText_prefix) ?: prefix
            val separatorStr = a.getString(R.styleable.FormattedNumberEditText_groupSeparator)
            if (!separatorStr.isNullOrEmpty()) {
                groupSeparator = separatorStr[0]
            }
            numberOfGroups = a.getInteger(R.styleable.FormattedNumberEditText_numberOfGroups, numberOfGroups)
            groupLength = a.getInteger(R.styleable.FormattedNumberEditText_groupLength, groupLength)
        }

        inputLength = numberOfGroups * (groupLength + 1) - 1
        separatorAndDigitsKeyListener = DigitsKeyListener.getInstance("0123456789$groupSeparator")

        setText(prefix)
        setSelection(text!!.length)
        inputType = InputType.TYPE_CLASS_NUMBER
        keyListener = digitsKeyListener
        addTextChangedListener(TextChangeListener())

        initCompleted = true
    }

    override fun onSelectionChanged(start: Int, end: Int) {
        if (!initCompleted) {
            return
        }

        // make sure input always starts with the prefix
        if (!text!!.startsWith(prefix)) {
            setText(prefix)
            setSelection(text!!.length, text!!.length)
            return
        }

        // make sure cursor is always at the end of the string
        if (start != text!!.length || end != text!!.length) {
            setSelection(text!!.length)
        } else {
            super.onSelectionChanged(start, end)
        }
    }

    private inner class TextChangeListener : TextWatcher {

        var textBefore = ""
        var enteredText = ""
        var deletedChars = 0

        var listenerEnabled = true

        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
            if (!listenerEnabled) return

            textBefore = text.toString()
            enteredText = ""
            deletedChars = 0
        }

        override fun onTextChanged(text: CharSequence?, start: Int, lengthBefore: Int, lengthAfter: Int) {
            if (!listenerEnabled) return

            if (text == null) {
                deletedChars = textBefore.length
                return
            }

            if (text.length < textBefore.length) {
                deletedChars = textBefore.length - text.length
                return
            }

            enteredText = text.toString().substring(textBefore.length, text.length)
        }

        override fun afterTextChanged(s: Editable?) {
            if (!listenerEnabled) return

            if (s == null) {
                return
            }

            listenerEnabled = false

            if (deletedChars > 0) {
                handleTextChange(s)
            } else {
                if (enteredText.length > 1) {
                    s.replace(s.length - enteredText.length, s.length, "")

                    // Append one char at a time
                    enteredText.forEach {
                        s.append("$it")
                        handleTextChange(s)
                    }
                } else {
                    handleTextChange(s)
                }
            }

            listenerEnabled = true
        }

        fun handleTextChange(s: Editable) {
            if (s.length > inputLength) {
                while (s.length > inputLength) {
                    s.delete(s.length - 1, s.length)
                }
            } else if (s.isNotEmpty() && s.length % (groupLength + 1) == 0) {
                if (s.last() == groupSeparator) {
                    s.delete(s.length - 1, s.length)
                } else if (s.last().isDigit() && s.length < inputLength) {
                    keyListener = separatorAndDigitsKeyListener
                    s.insert(s.length - 1, groupSeparator.toString())
                    keyListener = digitsKeyListener
                }
            }
        }

    }

}

attrs.xml(/ res / valuesに属します)

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="FormattedNumberEditText">
        <attr name="prefix" format="string" />
        <attr name="numberOfGroups" format="integer" />
        <attr name="groupLength" format="integer" />
        <attr name="groupSeparator" format="string" />
    </declare-styleable>
</resources>

使用例

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="16dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Credit card number" />

    <com.example.myapplication.FormattedNumberEditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="Credit card number (different separator)" />

    <com.example.myapplication.FormattedNumberEditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:groupSeparator="-" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="Phone number starting with +370" />

    <com.example.myapplication.FormattedNumberEditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:groupLength="13"
        app:groupSeparator=" "
        app:numberOfGroups="1"
        app:prefix="+370\u0020" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="IBAN number starting with LT" />

    <com.example.myapplication.FormattedNumberEditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:groupLength="4"
        app:groupSeparator=" "
        app:numberOfGroups="5"
        app:prefix="LT" />

</LinearLayout>
于 2019-10-14T14:55:35.337 に答える
11

私は自分のソリューションをリストに追加しています。私の知る限り、欠点はありません。途中で編集したり、間隔の文字を削除したり、コピーして貼り付けたりすることができます。

文字列内の任意の場所で編集を実行できるようにし、カーソル位置を維持するために、編集可能文字がトラバースされ、すべての空白(存在する場合)が1つずつ削除されます。次に、新しい空白が適切な位置に追加されます。これにより、コンテンツに加えられた変更に合わせてカーソルが移動します。

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;
    }
}
于 2015-04-21T13:48:40.670 に答える
8

TextWatcherを使用するのが正しいかどうかわからない-InputFilterを使用する必要があります

Androidのドキュメントによると、外部での使用例としてTextWatcherを使用する必要があります。パスワード入力用の1つの[EditView] + 「弱い」、「強い」などを表示する1つの[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クラスで実行する必要があります

それが役に立てば幸い !

于 2017-03-07T17:05:36.533 に答える
6

次の実装を行ったばかりで、の任意の位置に新しいテキストを貼り付けて入力しても、うまく機能します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());

于 2016-03-14T21:59:45.387 に答える
5

この実装により、ユーザーが文字列の途中で編集した場合でも、間隔文字が正しく配置されます。ソフトキーボードに表示される他の文字(ダッシュなど)もサポートされています。つまり、ユーザーはそれらを入力できません。行われる可能性のある1つの改善:この実装では、文字列の途中にあるスペーシング文字を削除できません。

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ディジットをマスクするための適切な実装でうまく機能します。

于 2015-02-02T01:35:16.713 に答える
5

Kotlinを使用している場合、これは役立つ場合があります。

class CreditCardTextFormatter(
    private var separator: String = " - ",
    private var divider: Int = 5
) : TextWatcher {

    override fun afterTextChanged(s: Editable?) {
        if (s == null) {
            return
        }
        val oldString = s.toString()
        val newString = getNewString(oldString)
        if (newString != oldString) {
            s.replace(0, oldString.length, getNewString(oldString))
        }
    }

    private fun getNewString(value: String): String {

        var newString = value.replace(separator, "")

        var divider = this.divider
        while (newString.length >= divider) {
            newString = newString.substring(0, divider - 1) + this.separator + newString.substring(divider - 1)
            divider += this.divider + separator.length - 1
        }
        return newString
    }

    override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
    }

    override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
    }
}

XML:

<EditText
        android:id="@+id/etCardNumber"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:digits="0123456789- "
        android:inputType="number"
        android:hint="____ - ____ - ____ - ____"
        android:maxLength="25" />

そしてそれを使用する方法:

etCardNumber.addTextChangedListener(CreditCardTextFormatter())
于 2020-03-05T18:28:06.537 に答える
4

私のソリューションは、ミドルテキスト操作やコピーアンドペースト操作でうまく機能すると思います。

以下のコードをご覧ください。

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

レイアウトファイル内の他の文字を避けるために、inputTypeをEditTextの数値にします。

それがあなたのお役に立てば幸いです。

于 2018-08-07T05:21:17.870 に答える
3

このプロジェクトをご覧ください。Androidフォーム編集テキストはEditTextの拡張であり、データ検証機能を編集テキストにもたらします

于 2012-08-03T06:12:12.723 に答える
3

たくさん検索して、自分のニーズを満たす満足のいく答えが得られなかった後、私は自分の関数を書くことになりました。

入力するカードの種類に基づいて、入力したクレジットカードの詳細をフォーマットする例を次に示します。現在、フォーマットの目的でVisa、MasterCard、AmericanExpressを処理しています。

    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;

それが役に立てば幸い!

于 2015-01-21T10:21:16.077 に答える
1

あなたはすでにそれを理解しているかもしれませんが、これが私がしたことです。私がオーバーライドしなければならなかった唯一のメソッドはAfterTextChangedでした。

クレジットカードのフォームがすでに有効であるかどうかを確認します。無限の再帰を防ぐための基本ケースです。

フォームが無効な場合は、すべての空白を削除し、別の文字列にコピーして、必要に応じて空白を挿入します。

次に、編集可能な文字列を新しい文字列に置き換えるだけです。

特定のステップのコードが必要な場合は、お気軽にお問い合わせください。

また、Preethi、スペースを削除できない理由は、onTextChangedコールバックでテキストを変更できないためです。開発者サイトから:

public abstract void onTextChanged(CharSequence s、int start、int before、int count)APIレベル1に追加されました

このメソッドは、s内で、開始から始まるカウント文字が、以前の長さの古いテキストを置き換えたことを通知するために呼び出されます。このコールバックからsに変更を加えようとするとエラーになります。

于 2013-01-05T16:55:13.147 に答える
1
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) {
        }
    });
}
于 2014-01-17T03:52:19.063 に答える
1

これは、すべての関数を適切に使用して決定を下す例です。コードは少し長くなる可能性がありますが、主に関数で指定された値(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.
    }
}

}

于 2014-10-23T01:37:43.690 に答える
1

これが私の解決策です。私のコメントは、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;
        }
    }
}
于 2015-11-06T14:38:37.743 に答える
1

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

}
于 2017-03-28T10:34:58.297 に答える
1

上記の答えはどれも私にとって完璧ではありません。start-string / end-string/mid-stringの問題を解決するものを作成しました。コピー&ペーストも正常に機能するはずです。これは、Mastercard、Visa、Amexをサポートしています。セパレータを変更できます。お支払い方法の種類が不要な場合は、削除してください。でもKotlinです。アイデアは単純です。テキストが変更されるたびに、すべての区切り文字を削除し、フォーマットに基づいて再度追加しました。は、start-string/mid-stringの問題を解決します。次に、唯一の問題は、区切り文字を追加した後、正しいテキスト位置を計算する必要があることです。

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
}
于 2018-01-22T23:16:01.457 に答える
1

このソリューションは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();
    }
}
于 2018-02-27T03:47:22.480 に答える
1

まだ答えを探している人がいたら、

1行のコードでテキストを自動フォーマットするには、format-edit-textライブラリを試してください。このライブラリは、ダッシュを使用して入力の形式を定義します。

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

使い方

app/build.gradleにformat-edit-textライブラリの依存関係を追加します

implementation 'com.androidwidgets:formatedittext:0.2.0'

activity_main.xmlにFormatEditTextビューを追加します

<?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>

MainActivity.javaでクレジットカードの形式をFormatEditTextビューに設定します

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

これにより、以下の出力が生成されます

PS:パラメータinputTypeがレイアウトファイルのFormatEditTextビューに追加されていることを確認してください。

android:inputType="number"
于 2020-01-14T22:09:18.880 に答える
1

私はこの質問が少し古いことを知っていますが、私はIBANのためにこれを実装する必要があり、与えられた答えに満足していません。だから私はこれのためにいくつかのコードを書いた。ただし、「パターン」と「ディバイダー」をパラメーターとして使用するため、クレジットカード番号にも使用できます。

これは拡張テキストウォッチャークラスです。

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

public class IbanTextWatcher implements TextWatcher {

    private int[] pattern;
    private String divider;
    private String before;
    private EditText field;
    private boolean dividerDeleted;
    

    public IbanTextWatcher(int[] pattern, String divider, EditText field) {
        this.divider = divider;
        this.pattern = pattern;
        this.field = field;
    }


    @Override
    public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
        before = charSequence.toString();

        if (!String.valueOf(charSequence).equals("") && charSequence.length() > i) {
            if (String.valueOf(before.charAt(i)).equals(getDivider())) {
                dividerDeleted = true;
            } else {
                dividerDeleted = false;
            }
        }
    }

    @Override
    public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {

    }

    @Override
    public void afterTextChanged(Editable editable) {

        String input = editable.toString().replaceAll("\\s", "");
        StringBuilder output = new StringBuilder();
        boolean error = false;
        int currentIndex = 0;
        int cursorPosition = getField().getSelectionStart();
        int lengthBefore;
        int currentPatternMember = 0;
        
        //prevent user to delete the divider
        if (dividerDeleted && cursorPosition != getField().getText().length()) {
            getField().setText(getBefore());
            getField().setSelection(cursorPosition + 1);
            return;
        } else if (input.equals(getBefore().replaceAll("\\s", ""))) {
            return;
        }

        for (int i = 0; i < getPattern().length; i++) {
            error = false;
            currentPatternMember = getPattern()[i];
            try {
                output.append(input.substring(currentIndex, currentIndex + currentPatternMember));
            } catch (StringIndexOutOfBoundsException e) {
                error = true;
            }

            if (!error) {
                if (i != getPattern().length - 1) {
                    output.append(getDivider());
                }
                currentIndex += currentPatternMember;
            } else {
                break;
            }
        }

        if (error) {
            output.append(input.substring(currentIndex, input.length()));
        } 

        cursorPosition = getField().getSelectionStart();
        lengthBefore = getBefore().length();
        getField().setText(output.toString());

        if (cursorPosition != lengthBefore && cursorPosition != lengthBefore + 1) {
            getField().setSelection(cursorPosition);
        } else {
            getField().setSelection(getField().getText().length());
        }
    }

    public int[] getPattern() {
        return pattern;
    }

    public String getDivider() {
        return divider;
    }

    public String getBefore() {
        return before;
    }

    public EditText getField() {
        return field;
    }
}

そして、これは私がそれを使用する方法です:

int[] pattern = {2,4,4,4,4,4,2}; // 
iban.addTextChangedListener(new IbanTextWatcher(pattern, " ", iban)); //here iban is my edittext field

ちなみに、フィールドの最大長はxmlで設定しました。

于 2020-03-11T06:29:33.080 に答える
0

レイアウト内:

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

ここではTextWachter、16桁のクレジットカードの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)
    }

}
于 2018-06-12T16:06:32.927 に答える
0
 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());
于 2018-09-03T12:22:56.527 に答える
0
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)
}

}
于 2018-12-19T05:27:09.097 に答える
0

これは、 Igor 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)
    }
  }
}

于 2019-08-15T03:52:17.503 に答える
0

1.このクラスをコピーして貼り付けます

class EditTextForCards @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = androidx.appcompat.R.attr.editTextStyle
) : AppCompatEditText(context, attrs, defStyleAttr) {

    private var mCCPatterns = SparseArray<Pattern>()
    private var mSeparator: Separator = Separator.NONE
    private var mDrawableGravity: Gravity? = null/*Gravity.END*/
    private var isValidCard: Boolean = false
    private var mCurrentDrawableResId = Card.UNKNOWN.drawableRes

    val textWithoutSeparator
        get() = if (mSeparator == Separator.NONE) {
            text.toString()
        } else {
            text.toString().replace(mSeparator.toRegex(), "")
        }

    val isCardValid: Boolean
        get() = textWithoutSeparator.length > 12 && isValidCard

    val cardType: Card
        get() = Card.from(mCurrentDrawableResId)

    enum class Separator(private val stringValue: String) {
        NONE(""), SPACES(" "), DASHES("-");

        override fun toString() = stringValue

        internal fun toRegex() = stringValue.toRegex()

        internal val length
            get() = stringValue.length
    }

    enum class Gravity {
        START, END, LEFT, RIGHT
    }

    enum class Card(internal val value: Int, @field:DrawableRes internal val drawableRes: Int) {
        VISA(1, R.drawable.ic_visa),
        MASTERCARD(2, R.drawable.ic_mastercard),
        AMEX(4, R.drawable.amex),
        DISCOVER(8, R.drawable.discover),
        UNKNOWN(-1, R.drawable.ic_visa);

        companion object {
            internal fun from(@DrawableRes drawableRes: Int): Card {
                for (card in values()) {
                    if (card.drawableRes == drawableRes) {
                        return card
                    }
                }
                return UNKNOWN
            }
        }
    }

    private val textWatcher = object : TextWatcher {
        override fun afterTextChanged(s: Editable?) {}

        override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {}

        override fun onTextChanged(
            text: CharSequence,
            start: Int,
            lengthBefore: Int,
            lengthAfter: Int
        ) {
            val textWithoutSeparator = textWithoutSeparator

            var mDrawableResId = 0
            for (i in 0 until mCCPatterns.size()) {
                val key = mCCPatterns.keyAt(i)

                val p = mCCPatterns.get(key)

                val m = p.matcher(textWithoutSeparator)
                isValidCard = m.find()
                if (isValidCard) {
                    mDrawableResId = key
                    break
                }
            }
//            if (mDrawableResId != 0 && mDrawableResId != mCurrentDrawableResId) {
//                mCurrentDrawableResId = mDrawableResId
//            } else if (mDrawableResId == 0) {
//                mCurrentDrawableResId = Card.UNKNOWN.drawableRes
//            }
//            addDrawable()
            addSeparators()
        }
    }

    init {
        setDisabledCards()
        inputType = InputType.TYPE_CLASS_PHONE
        setSeparator(Separator.NONE)
//        setDrawableGravity(Gravity.END)
        attrs?.let { applyAttributes(it) }
        addTextChangedListener(textWatcher)
    }

    private fun applyAttributes(attrs: AttributeSet) {
        val a = context.theme.obtainStyledAttributes(
            attrs,
            R.styleable.EditTextForCards,
            0, 0
        )
        try {
            setSeparator(
                Separator.values()[a.getInt(
                    R.styleable.EditTextForCards_separator,
                    Separator.NONE.ordinal
                )]
            )
            setDisabledCardsInternal(a.getInt(R.styleable.EditTextForCards_disabledCards, 0))
            setDrawableGravity(
                Gravity.values()[a.getInt(
                    R.styleable.EditTextForCards_drawableGravity,
                    Gravity.END.ordinal
                )]
            )
        } finally {
            a.recycle()
        }
    }

    private fun addDrawable() {
        var currentDrawable = ContextCompat.getDrawable(context, mCurrentDrawableResId)
        if (currentDrawable != null && error.isNullOrEmpty()) {
            currentDrawable = resize(currentDrawable)
            when (mDrawableGravity) {
                Gravity.START -> setDrawablesRelative(start = currentDrawable)
                Gravity.RIGHT -> setDrawables(right = currentDrawable)
                Gravity.LEFT -> setDrawables(left = currentDrawable)
                else -> setDrawablesRelative(end = currentDrawable)
            }
        }
    }

    private fun addSeparators() {
        val text = text.toString()
        if (mSeparator != Separator.NONE) {
            if (text.length > 4 && !text.matches("(?:[0-9]{4}$mSeparator)+[0-9]{1,4}".toRegex())) {
                val sp = StringBuilder()
                val caretPosition = selectionEnd
                val segments = splitString(text.replace(mSeparator.toRegex(), ""))
                for (segment in segments) {
                    sp.append(segment).append(mSeparator)
                }
                setText("")
                append(sp.delete(sp.length - mSeparator.length, sp.length).toString())
                if (caretPosition < text.length)
                    setSelection(caretPosition)
            }
        }
    }

    private fun removeSeparators() {
        var text = text.toString()
        text = text.replace(" ".toRegex(), "").replace("-".toRegex(), "")
        setText("")
        append(text)
    }

    private fun splitString(s: String): Array<String?> {
        val arrayLength = ceil(s.length / 4.toDouble()).toInt()
        val result = arrayOfNulls<String>(arrayLength)

        var j = 0
        val lastIndex = result.size - 1
        for (i in 0 until lastIndex) {
            result[i] = s.substring(j, j + 4)
            j += 4
        }
        result[lastIndex] = s.substring(j)

        return result
    }

    /*@Deprecated("Please use the method that accepts a Separator enum instead.", ReplaceWith("this.setSeparator(Separator.)"))
    fun setSeparator(@IntRange(from = 0, to = 2) separator: Int) {
        require(!(separator > 2 || separator < 0)) {
            "The separator has to be one of the following:" +
                    "NO_SEPARATOR." +
                    "SPACES_SEPARATOR." +
                    "DASHES_SEPARATOR."
        }
        setSeparator(Separator.values()[separator])
    }*/

    /**
     * Use this method to set the separator style.
     * The default separator is [Separator.NONE].
     *
     * @param separator the style of the separator.
     */
    fun setSeparator(separator: Separator) {
        mSeparator = separator
        if (mSeparator != Separator.NONE) {
            filters = arrayOf<InputFilter>(InputFilter.LengthFilter(23))
            keyListener = DigitsKeyListener.getInstance("0123456789$mSeparator")
            addSeparators()
        } else {
            filters = arrayOf<InputFilter>(InputFilter.LengthFilter(19))
            keyListener = DigitsKeyListener.getInstance("0123456789")
            removeSeparators()
        }
    }

    /**
     * Use this method to set the location of the card drawable.
     * The default gravity is [Gravity.END].
     *
     * @param gravity the drawable location.
     */
    fun setDrawableGravity(gravity: Gravity) {
        mDrawableGravity = gravity
        addDrawable()
    }

    private fun setDisabledCardsInternal(disabledCards: Int) {
        val cards = ArrayList<Card>()
        if (containsFlag(disabledCards, Card.VISA.value)) {
            cards.add(Card.VISA)
        }
        if (containsFlag(disabledCards, Card.MASTERCARD.value)) {
            cards.add(Card.MASTERCARD)
        }
        /*if (containsFlag(disabledCards, Card.AMEX.value)) {
            cards.add(Card.AMEX)
        }
        if (containsFlag(disabledCards, Card.DISCOVER.value)) {
            cards.add(Card.DISCOVER)
        }*/
        setDisabledCards(*cards.toTypedArray())
    }

    @Deprecated(
        "Please use the method that accepts an array of Cards instead.",
        ReplaceWith("this.setDisabledCards(cards)")
    )
    fun setDisabledCards(disabledCards: Int) {
        setDisabledCardsInternal(disabledCards)
    }

    /**
     * Use this method to set which cards are disabled.
     * By default all supported cards are enabled.
     *
     * @param cards the cards to be disabled.
     */
    fun setDisabledCards(vararg cards: Card) {
        var disabledCards = 0
        for (card in cards) {
            disabledCards = disabledCards or card.value
        }
        mCCPatterns.clear()
        if (!containsFlag(disabledCards, Card.VISA.value)) {
            mCCPatterns.put(Card.VISA.drawableRes, Pattern.compile("^4[0-9]{1,12}(?:[0-9]{6})?$"))
        }
        if (!containsFlag(disabledCards, Card.MASTERCARD.value)) {
            mCCPatterns.put(Card.MASTERCARD.drawableRes, Pattern.compile("^5[1-5][0-9]{0,14}$"))
        }
        /*if (!containsFlag(disabledCards, Card.AMEX.value)) {
            mCCPatterns.put(Card.AMEX.drawableRes, Pattern.compile("^3[47][0-9]{0,13}$"))
        }
        if (!containsFlag(disabledCards, Card.DISCOVER.value)) {
            mCCPatterns.put(Card.DISCOVER.drawableRes, Pattern.compile("^6(?:011|5[0-9]{1,2})[0-9]{0,12}$"))
        }*/
        textWatcher.onTextChanged("", 0, 0, 0)
    }

    private fun containsFlag(flagSet: Int, flag: Int): Boolean {
        return flagSet or flag == flagSet
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        var noDrawablesVisible = true
        for (drawable in compoundDrawables) {
            if (drawable != null) {
                noDrawablesVisible = false
                break
            }
        }
        if (noDrawablesVisible) {
            addDrawable()
        }
    }

    private fun resize(image: Drawable) =
        when (val height = measuredHeight - (paddingTop + paddingBottom)) {
            in 1 until image.intrinsicHeight -> {
                val bitmap = (image as BitmapDrawable).bitmap
                val ratio = image.getIntrinsicWidth().toFloat() / image.intrinsicHeight.toFloat()
                val resizedBitmap =
                    Bitmap.createScaledBitmap(bitmap, (height * ratio).toInt(), height, false)
                resizedBitmap.density = Bitmap.DENSITY_NONE
                BitmapDrawable(resources, resizedBitmap)
            }
            in Int.MIN_VALUE..0 -> null
            else -> image
        }

    private fun setDrawablesRelative(
        start: Drawable? = null,
        top: Drawable? = null,
        end: Drawable? = null,
        bottom: Drawable? = null
    ) =
        /*TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(this, start, top, end, bottom)*/
        TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(this, null, null, null, null)

    private fun setDrawables(
        left: Drawable? = null,
        top: Drawable? = null,
        right: Drawable? = null,
        bottom: Drawable? = null
    ) =
        /*setCompoundDrawablesWithIntrinsicBounds(left, top, right, bottom)*/
        setCompoundDrawablesWithIntrinsicBounds(null, null, null, null)

    companion object {
        @Deprecated("This constant has been replace with an enum.", ReplaceWith("Separator.NONE"))
        const val NO_SEPARATOR = 0

        @Deprecated("This constant has been replace with an enum.", ReplaceWith("Separator.SPACES"))
        const val SPACES_SEPARATOR = 1

        @Deprecated("This constant has been replace with an enum.", ReplaceWith("Separator.DASHES"))
        const val DASHES_SEPARATOR = 2

        @Deprecated("This constant has been replace with an enum.", ReplaceWith("null"))
        const val NONE = 0

        @Deprecated("This constant has been replace with an enum.", ReplaceWith("Card.VISA"))
        const val VISA = 1

        @Deprecated("This constant has been replace with an enum.", ReplaceWith("Card.MASTERCARD"))
        const val MASTERCARD = 2

        @Deprecated("This constant has been replace with an enum.", ReplaceWith("Card.AMEX"))
        const val AMEX = 4

        @Deprecated("This constant has been replace with an enum.", ReplaceWith("Card.DISCOVER"))
        const val DISCOVER = 8
    }
}

2.このスタイルを貼り付けます

<declare-styleable name="EditTextForCards">
    <attr name="separator" format="enum">
        <enum name="no_separator" value="0" />
        <enum name="spaces" value="1" />
        <enum name="dashes" value="2" />
    </attr>
    <attr name="disabledCards">
        <flag name="none" value="0" />
        <flag name="visa" value="1" />
        <flag name="mastercard" value="2" />
        <flag name="amex" value="4" />
        <flag name="discover" value="8" />
    </attr>
    <attr name="drawableGravity">
        <enum name="start" value="0" />
        <enum name="end" value="1" />
        <enum name="left" value="2" />
        <enum name="right" value="3" />
    </attr>
</declare-styleable>

3.レイアウトファイルで、

<EditTextForCards
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_margin="@dimen/dp_5"
        android:digits="0123456789 "
        android:hint="@string/card_number"
        android:padding="@dimen/dp_20"
        android:textColor="@android:color/white"
        android:textColorHint="@android:color/white"
        android:textSize="@dimen/sp_16"
        app:separator="spaces" />
于 2020-09-01T08:29:30.673 に答える
-1
import android.text.Editable;
import android.text.TextWatcher;
import android.widget.EditText;`

public class CreditCard implements TextWatcher
{
    EditText editText;

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

    @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) {
        try
        {
            editText.removeTextChangedListener(this);

            String str = editText.getText().toString().replaceAll("-", "");

            editText.setText(setDash(str));

            editText.setSelection(editText.getText().toString().length());

            editText.addTextChangedListener(this);
            return;
        }

        catch (Exception ex)
        {
            ex.printStackTrace();
            editText.addTextChangedListener(this);
        }

    }

    public static String setDash(String value)
    {
        String str = "";
        int j = 0;

        for (int i = 0;i<value.length(); i++)
        {
            j++;

            if (j == 5)
            {
                str = str+"-";
                j = 1;
            }

            str = str + value.charAt(i);
        }

        return str;

    }

    public static String trimDashOfString(String string)
    {
        if (string.contains("-")) {
            return string.replace("-", "");
            } else {
            return string;
        }

    }
}
于 2020-02-20T13:31:55.783 に答える