Как создать для текстового поля маску ввода?

Нужно, чтобы пользователь вводил в текстовое поле цифры даты в формате xx.xx.xxxx (то есть чтобы точки уже стояли и был возможен ввод только цифры). Дополнительно чтобы была проверка на ввод даты - то есть что бы не вводили 32.03.2021, а также если год не високосный, то февраль могли бы ввести только 28.02.2011, а не 29.02.2011 Как это можно сделать? (чтобы не использовать DataPicker)


Ответы (2 шт):

Автор решения: Виталий Робиновский

К примеру ваш EditText

 <com.google.android.material.textfield.TextInputEditText
                    android:id="@+id/ac_registration_et_birth_date"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:inputType="number"
                    android:hint="ДД.ММ.ГГГГ" />

Мы можем создать класс DateInputMask, который выполнит нашу задачу

class DateInputMask(private val input: EditText, private val saveDate: (dateTitle: String) -> Unit) : TextWatcher {

    private var current = ""
    private val ddmmyyyy = "DDMMYYYY"
    private val cal: Calendar = Calendar.getInstance()
    override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
    override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
        if (s.toString() == current) {
            return
        }

        var clean = s.toString().replace("[^0-9]".toRegex(), "")
        val cleanC = current.replace("[^0-9]".toRegex(), "")
        val cl = clean.length
        var sel = cl
        var i = 2
        while (i <= cl && i < 6) {
            sel++
            i += 2
        }
        //Fix for pressing delete next to a forward slash
        if (clean == cleanC) sel--
        if (clean.length < 8) {
            clean += ddmmyyyy.substring(clean.length)
        } else {
            //This part makes sure that when we finish entering numbers
            //the date is correct, fixing it otherwise
            var day = clean.substring(0, 2).toInt()
            var mon = clean.substring(2, 4).toInt()
            var year = clean.substring(4, 8).toInt()
            mon = if (mon < 1) 1 else if (mon > 12) 12 else mon
            cal.set(Calendar.MONTH, mon - 1)
            year = if (year < 1900) 1900 else if (year > 2020) 2020 else year
            cal.set(Calendar.YEAR, year)

            day = if (day > cal.getActualMaximum(Calendar.DATE)) cal.getActualMaximum(Calendar.DATE) else day
            clean = String.format("%02d%02d%02d", day, mon, year)
        }
        clean = String.format("%s.%s.%s", clean.substring(0, 2),
                clean.substring(2, 4),
                clean.substring(4, 8))
        sel = if (sel < 0) 0 else sel
        current = clean
        input.setText(current)
        input.setSelection(if (sel < current.length) sel else current.length)

        saveDate(if (sel == 10) input.text.toString() else "")
    }

    override fun afterTextChanged(s: Editable) {}

    init {
        input.addTextChangedListener(this)
    }
}

И осталось нам подключить его к нашему EditText

  DateInputMask(ac_registration_et_birth_date) { it ->
             //Ваша логика
            
        }
→ Ссылка
Автор решения: Vadik

Чтобы проверить корректность введенной пользователем даты, можно написать такую функцию:

fun parseLocalDateOrNull(text: String, formatter: DateTimeFormatter) =
    try {
        LocalDate.parse(text, formatter)
    } catch (ex: DateTimeParseException) {
        null
    }

Как видно из названия, функция вернет дату либо null, если дата невалидная:

val formatter = DateTimeFormatter.ofPattern("dd.MM.uuuu")
    .withResolverStyle(ResolverStyle.STRICT)
val date = parseLocalDateOrNull("31.02.1970", formatter)
if (date == null) {
    // введена некорректная дата 
}

Класс LocalDate доступен только для Android API 26 и выше. Для старых версий Андроида можно воспользоваться библиотекой Joda-Time.

→ Ссылка