Я хочу, чтобы пользователь ввел номер телефона в editText для динамического изменения формата каждый раз, когда пользователь вводит номер. То есть, когда пользователь вводит до 4 цифр, например 7144, editText показывает «714-4». Я хотел бы, чтобы editText динамически обновлялся в формате ###-###-#### всякий раз, когда пользователь вводит цифру. Как это может быть сделано? Кроме того, я обрабатываю более одного editTexts.
Замаскируйте EditText форматом номера телефона NaN, как в PhoneNumberUtils
Ответы (8)
Самый простой способ сделать это — использовать встроенный в Android PhoneNumberFormattingTextWatcher.
Итак, в основном вы получаете свой EditText в коде и устанавливаете свой текстовый наблюдатель следующим образом...
EditText inputField = (EditText) findViewById(R.id.inputfield);
inputField.addTextChangedListener(new PhoneNumberFormattingTextWatcher());
Хорошая вещь в использовании PhoneNumberFormattingTextWatcher заключается в том, что он будет правильно форматировать ввод вашего номера в зависимости от вашей локали.
Ответ выше правильный, но он работает с конкретной страной. если кому-то нужен такой отформатированный номер телефона (###-###-####). Затем используйте это:
etPhoneNumber.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
int digits = etPhoneNumber.getText().toString().length();
if (digits > 1)
lastChar = etPhoneNumber.getText().toString().substring(digits-1);
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
int digits = etPhoneNumber.getText().toString().length();
Log.d("LENGTH",""+digits);
if (!lastChar.equals("-")) {
if (digits == 3 || digits == 7) {
etPhoneNumber.append("-");
}
}
}
@Override
public void afterTextChanged(Editable s) {
}
});
Объявите String lastChar = " "
в своей деятельности.
Теперь добавьте эту строку в xml вашего edittext
android:inputType="phone"
Это все.
Отредактировано: если вы хотите, чтобы длина редактируемого текста не превышала 10 цифр, добавьте строку ниже:
android:maxLength="12"
(Это 12, потому что "-" займет пробел два раза)
Просто добавьте следующее в EditText для номера телефона, чтобы получить отформатированный номер телефона (###-###-####)
Phone.addTextChangedListener(new TextWatcher() {
int length_before = 0;
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
length_before = s.length();
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
if (length_before < s.length()) {
if (s.length() == 3 || s.length() == 7)
s.append("-");
if (s.length() > 3) {
if (Character.isDigit(s.charAt(3)))
s.insert(3, "-");
}
if (s.length() > 7) {
if (Character.isDigit(s.charAt(7)))
s.insert(7, "-");
}
}
}
});
Мой скрипт, пример взят отсюда описание здесь
<android.support.design.widget.TextInputLayout
android:id="@+id/numphone_layout"
app:hintTextAppearance="@style/MyHintText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp">
<android.support.design.widget.TextInputEditText
android:id="@+id/edit_text_numphone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/MyEditText"
android:digits="+() 1234567890-"
android:hint="@string/hint_numphone"
android:inputType="phone"
android:maxLength="17"
android:textSize="14sp" />
</android.support.design.widget.TextInputLayout>
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextInputEditText phone = (TextInputEditText) findViewById(R.id.edit_text_numphone);
//Add to mask
phone.addTextChangedListener(textWatcher);
}
TextWatcher textWatcher = new TextWatcher() {
private boolean mFormatting; // this is a flag which prevents the stack overflow.
private int mAfter;
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// nothing to do here..
}
//called before the text is changed...
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
//nothing to do here...
mAfter = after; // flag to detect backspace..
}
@Override
public void afterTextChanged(Editable s) {
// Make sure to ignore calls to afterTextChanged caused by the work done below
if (!mFormatting) {
mFormatting = true;
// using US or RU formatting...
if(mAfter!=0) // in case back space ain't clicked...
{
String num =s.toString();
String data = PhoneNumberUtils.formatNumber(num, "RU");
if(data!=null)
{
s.clear();
s.append(data);
Log.i("Number", data);//8 (999) 123-45-67 or +7 999 123-45-67
}
}
mFormatting = false;
}
}
};
В приведенных выше решениях не учитывается возврат, поэтому, когда вы удаляете некоторые числа после ввода, формат может испортиться. Код ниже исправляет эту проблему.
phoneNumberEditText.addTextChangedListener(new TextWatcher() {
int beforeLength;
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
beforeLength = phoneNumberEditText.length();
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
int digits = phoneNumberEditText.getText().toString().length();
if (beforeLength < digits && (digits == 3 || digits == 7)) {
phoneNumberEditText.append("-");
}
}
@Override
public void afterTextChanged(Editable s) { }
});
Этот код позволяет вводить номер телефона с маской ### - ### - #### (без пробелов), а также исправлена проблема с удалением цифр телефона:
editText.addTextChangedListener(new TextWatcher() {
final static String DELIMITER = "-";
String lastChar;
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
int digits = editText.getText().toString().length();
if (digits > 1)
lastChar = editText.getText().toString().substring(digits-1);
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
int digits = editText.getText().length();
// prevent input dash by user
if (digits > 0 && digits != 4 && digits != 8) {
CharSequence last = s.subSequence(digits - 1, digits);
if (last.toString().equals(DELIMITER))
editText.getText().delete(digits - 1, digits);
}
// inset and remove dash
if (digits == 3 || digits == 7) {
if (!lastChar.equals(DELIMITER))
editText.append("-"); // insert a dash
else
editText.getText().delete(digits -1, digits); // delete last digit with a dash
}
dataModel.setPhone(s.toString());
}
@Override
public void afterTextChanged(Editable s) {}
});
Макет:
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeOptions="actionDone"
android:textAlignment="textStart"
android:inputType="number"
android:digits="-0123456789"
android:lines="1"
android:maxLength="12"/>
Динамическая маска для Android на языке Kotlin. Этот работает нормально и строго соответствует маске номера телефона. Вы можете предоставить любую маску по вашему желанию.
EDIT1: у меня есть новая версия, которая блокирует нежелательные символы, введенные пользователем на клавиатуре.
/**
* Text watcher allowing strictly a MASK with '#' (example: (###) ###-####
*/
class NumberTextWatcher(private var mask: String) : TextWatcher {
companion object {
const val MASK_CHAR = '#'
}
// simple mutex
private var isCursorRunning = false
private var isDeleting = false
override fun afterTextChanged(s: Editable?) {
if (isCursorRunning || isDeleting) {
return
}
isCursorRunning = true
s?.let {
val onlyDigits = removeMask(it.toString())
it.clear()
it.append(applyMask(mask, onlyDigits))
}
isCursorRunning = false
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
isDeleting = count > after
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {}
private fun applyMask(mask: String, onlyDigits: String): String {
val maskPlaceholderCharCount = mask.count { it == MASK_CHAR }
var maskCurrentCharIndex = 0
var output = ""
onlyDigits.take(min(maskPlaceholderCharCount, onlyDigits.length)).forEach { c ->
for (i in maskCurrentCharIndex until mask.length) {
if (mask[i] == MASK_CHAR) {
output += c
maskCurrentCharIndex += 1
break
} else {
output += mask[i]
maskCurrentCharIndex = i + 1
}
}
}
return output
}
private fun removeMask(value: String): String {
// extract all the digits from the string
return Regex("\\D+").replace(value, "")
}
}
EDIT 2: Модульные тесты
class NumberTextWatcherTest {
@Test
fun phone_number_test() {
val phoneNumberMask = "(###) ###-####"
val phoneNumberTextWatcher = NumberTextWatcher(phoneNumberMask)
val input = StringBuilder()
val expectedResult = "(012) 345-6789"
var result = ""
// mimic typing 10 digits
for (i in 0 until 10) {
input.append(i)
result = mimicTextInput(phoneNumberTextWatcher, result, i.toString()) ?: ""
}
Assert.assertEquals(input.toString(), "0123456789")
Assert.assertEquals(result, expectedResult)
}
@Test
fun credit_card_test() {
val creditCardNumberMask = "#### #### #### ####"
val creditCardNumberTextWatcher = NumberTextWatcher(creditCardNumberMask)
val input = StringBuilder()
val expectedResult = "0123 4567 8901 2345"
var result = ""
// mimic typing 16 digits
for (i in 0 until 16) {
val value = i % 10
input.append(value)
result = mimicTextInput(creditCardNumberTextWatcher, result, value.toString()) ?: ""
}
Assert.assertEquals(input.toString(), "0123456789012345")
Assert.assertEquals(result, expectedResult)
}
@Test
fun date_test() {
val dateMask = "####/##/##"
val dateTextWatcher = NumberTextWatcher(dateMask)
val input = "20200504"
val expectedResult = "2020/05/04"
val initialInputValue = ""
val result = mimicTextInput(dateTextWatcher, initialInputValue, input)
Assert.assertEquals(result, expectedResult)
}
@Test
fun credit_card_expiration_date_test() {
val creditCardExpirationDateMask = "##/##"
val creditCardExpirationDateTextWatcher = NumberTextWatcher(creditCardExpirationDateMask)
val input = "1121"
val expectedResult = "11/21"
val initialInputValue = ""
val result = mimicTextInput(creditCardExpirationDateTextWatcher, initialInputValue, input)
Assert.assertEquals(result, expectedResult)
}
private fun mimicTextInput(textWatcher: TextWatcher, initialInputValue: String, input: String): String? {
textWatcher.beforeTextChanged(initialInputValue, initialInputValue.length, initialInputValue.length, input.length + initialInputValue.length)
val newText = initialInputValue + input
textWatcher.onTextChanged(newText, 1, newText.length - 1, 1)
val editable: Editable = SpannableStringBuilder(newText)
textWatcher.afterTextChanged(editable)
return editable.toString()
}
}
Вот мое решение
Как запустить в Activity/Fragment (например, в onViewCreated):
//field in class
private val exampleIdValidator by lazy { ExampleIdWatcher(exampleIdField.editText!!) }
exampleIdField.editText?.addTextChangedListener(exampleIdValidator)
Класс проверки:
import android.text.Editable
import android.text.TextWatcher
import android.widget.EditText
class ExampleIdWatcher(exampleIdInput: EditText) : TextWatcher {
private var exampleIdInput: EditText = exampleIdInput
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(userInput: CharSequence?, start: Int, before: Int, count: Int) {
if (userInput!!.isNotEmpty() && !areSpacesCorrect(userInput)) {
val stringTextWithoutWhiteSpaces: String = userInput.toString().replace(" ", "")
val textSB: StringBuilder = StringBuilder(stringTextWithoutWhiteSpaces)
when {
textSB.length > 8 -> {
setSpacesAndCursorPosition(textSB, 2, 6, 10)
}
textSB.length > 5 -> {
setSpacesAndCursorPosition(textSB, 2, 6)
}
textSB.length > 2 -> {
setSpacesAndCursorPosition(textSB, 2)
}
}
}
}
override fun afterTextChanged(s: Editable?) {
}
private fun setSpacesAndCursorPosition(textSB: StringBuilder, vararg ts: Int) {
for (t in ts) // ts is an Array
textSB.insert(t, SPACE_CHAR)
val currentCursorPosition = getCursorPosition(exampleIdInput.selectionStart)
exampleIdInput.setText(textSB.toString())
exampleIdInput.setSelection(currentCursorPosition)
}
private fun getCursorPosition(currentCursorPosition: Int): Int {
return if (EXAMPLE_ID_SPACE_CHAR_CURSOR_POSITIONS.contains(currentCursorPosition)) {
currentCursorPosition + 1
} else {
currentCursorPosition
}
}
private fun areSpacesCorrect(userInput: CharSequence?): Boolean {
EXAMPLE_ID_SPACE_CHAR_INDEXES.forEach {
if (userInput!!.length > it && userInput[it].toString() != SPACE_CHAR) {
return false
}
}
return true
}
companion object {
private val EXAMPLE_ID_SPACE_CHAR_INDEXES: List<Int> = listOf(2, 6, 10)
private val EXAMPLE_ID_SPACE_CHAR_CURSOR_POSITIONS: List<Int> = EXAMPLE_ID_SPACE_CHAR_INDEXES.map { it + 1 }
private const val SPACE_CHAR: String = " "
}
}
Макет:
<com.google.android.material.textfield.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:digits=" 0123456789"
android:inputType="numberPassword"
android:maxLength="14"
tools:text="Example text" />
Результат:
XX XXX XXX XXX