Pre*_*thi 55 formatting android android-edittext
如何以EditText格式进行接受输入:
4digit 4digit 4digit 4digit
Run Code Online (Sandbox Code Playgroud)
我试过自定义格式编辑文本输入android接受信用卡号,但遗憾的是我无法删除空格.每当有空间时,我都无法删除它.请帮我找出问题所在.
Chr*_*ins 86
找到多个"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));
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后像其他任何一样将它添加到TextView中TextWatcher.
{
//...
mEditTextCreditCard.addTextChangedListener(new FourDigitCardFormatWatcher());
}
Run Code Online (Sandbox Code Playgroud)
这将自动删除合理返回的空间,以便用户在编辑时实际上可以减少击键次数.
如果你使用inputType="numberDigit"它将禁用' - '和''字符,所以我建议使用,inputType="phone".这可以启用其他字符,但只需使用自定义输入过滤器并解决问题.
Igo*_*nov 61
迟到的答案,但我想这可能对某人有帮助:
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;
}
});
Run Code Online (Sandbox Code Playgroud)
这与start-string/end-string/mid-string编辑完美配合,也可以完美粘贴.
Ran*_*ku' 21
我修改了Chris Jenkins的答案,使其更加健壮.这样,即使用户编辑文本的中间部分,间距字符仍然会正确插入(并在错误的位置自动删除).
要使其正常工作,请确保EditText属性设置如下(注意空格digits):
android:digits="01234 56789"
android:inputType="number"
android:maxLength="19"
Run Code Online (Sandbox Code Playgroud)
那么这就是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;
}
}
});
Run Code Online (Sandbox Code Playgroud)
小智 16
这是一个使用正则表达式的清洁解决方案 虽然正则表达式可以是低效率的,他们会因为它的处理最多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);
}
}
});
Run Code Online (Sandbox Code Playgroud)
Egi*_*gis 12
这是我用于信用卡号的类。下面的用法示例。
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
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
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>
Run Code Online (Sandbox Code Playgroud)
使用示例
<?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>
Run Code Online (Sandbox Code Playgroud)
MW.*_*MW. 11
我正在将我的解决方案添加到列表中.据我所知,它没有任何缺点; 您可以在中间编辑,删除间距字符,复制并粘贴到其中等.
为了允许在字符串中的任何位置进行编辑,并保持光标位置,遍历可编辑并且逐个取出所有空格(如果有的话).然后在适当的位置添加新的空格.这将确保光标随着对内容所做的更改一起移动.
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;
}
}
Run Code Online (Sandbox Code Playgroud)
即使用户编辑了mid-string,此实现也可确保正确放置间距字符.还支持在软键盘上显示的其他字符(例如破折号); 也就是说,用户无法输入它们.可以进行一项改进:此实现不允许删除字符串中间的字符.
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));
}
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
适用PasswordTransformationMethod于掩盖CC数字的适当实现.
我只是做了下一个实现,并且对我来说效果很好,即使在任何位置粘贴和键入新文本也是如此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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
用法: editText.addTextChangedListener(new CreditCardFormattingTextWatcher());
小智 5
不确定TextWatcher是否正确使用-我们应该使用InputFilter
据Android文档,TextWatcher应该用于外部使用例如:一个[EditView中用于密码输入+ 酮[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
}
}
Run Code Online (Sandbox Code Playgroud)
和长度过滤器(Android SDK中)相结合:
mEditCardNumber.setFilters(new InputFilter[]{
new InputFilter.LengthFilter(24),
new CreditCardInputFilter(),
});
Run Code Online (Sandbox Code Playgroud)
这个手柄输入的情况下和删除一个数字。
(!),但是,这不处理的情况下进行复制/一整串的粘贴,这应该在不同的输入过滤器类完成
希望能帮助到你 !
如果您使用 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) {
}
}
Run Code Online (Sandbox Code Playgroud)
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" />
Run Code Online (Sandbox Code Playgroud)
以及如何使用它:
etCardNumber.addTextChangedListener(CreditCardTextFormatter())
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
47140 次 |
| 最近记录: |