Skip to content

Instantly share code, notes, and snippets.

@trfiladelfo
Created August 14, 2018 14:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save trfiladelfo/11ce6a670893895d4433c7e1da07929d to your computer and use it in GitHub Desktop.
Save trfiladelfo/11ce6a670893895d4433c7e1da07929d to your computer and use it in GitHub Desktop.
Apply masks in Kotlin
/**
* Realiza a formatação instânea na caixa de texto conforme o formato pré-definido.
*
* Forma de uso:
* <code>
* textview_phone.let {
* it.addTextChangedListener(Tools.MaskWatcher(Tools.MaskWatcher.Format.MOBILE_PHONE))
* }
* </code>
* @param formats padrão para aplicar a máscara.
* @see Format
*/
class MaskWatcher(private vararg val formats: Format) : TextWatcher {
/**
* Enumerador com alguns formatos pré-definidos, mas existe a possibilidade
* de criar um formato personalizado.
* [x] Suporte somente para máscaras compostas de caracteres 9
* [x] Reconhecimento de caracteres numérico
* [] Reconhecimento de caracteres alfanúmerico
*
* Aviso:
* Lembre-se de que se usar uma máscara que conter algum caracter não previsto
* no input de teclado, inserir na tag digits para ser inputado na caixa de texto.
* Por exemplo - CPF ("999.999.999-99")
* <code>
* <EditText
* android:digits="0123456789.-/"
* android:inputType="number"/>
* </code>
*
* Caso não inserir os caracteres ponto, hífen e barra na tag digits, a máscara não irá
* funcionar, pois esses caracteres serão ignorados durante a inserção automática.
*/
enum class Format(private val mask: String){
CPF ("999.999.999-99"),
CNPJ("99.999.999/9999-99"),
MOBILE_PHONE ("(99)9-9999-9999"),
PHONE("(99)9999-9999"),
ZIPCODE("99999-999"),
DATETIME_SHORT("99/99/9999 99:99"),
DATETIME_LONG("99/99/9999 99:99:99"),
HOUR("99:99"),
DATE("99/99/9999");
override fun toString(): String = mask
}
private var isRunning = false
private var isDeleting = false
private lateinit var defaultMask: Format
init {
formats.sortBy { it.toString().length }
defaultMask = formats.first()
}
/**
* @see TextWatcher.beforeTextChanged
*/
override fun beforeTextChanged(charSequence: CharSequence, start: Int, count: Int, after: Int) {
isDeleting = count > after
}
/**
* @see TextWatcher.onTextChanged
*/
override fun onTextChanged(charSequence: CharSequence, start: Int, before: Int, count: Int) {
}
/**
* @see TextWatcher.afterTextChanged
*/
override fun afterTextChanged(editable: Editable) {
if (isRunning || isDeleting) {
return
}
isRunning = true
applyMask(editable)
isRunning = false
}
/**
* Aplica a máscara mais adequada no texto inserido
*
* @param editable texto inserido na caixa de texto
*/
private fun applyMask(editable: Editable) {
val format = getFormat(editable, formats)
if (format == defaultMask) {
applyFormat(editable, format)
} else {
defaultMask = format
val text = editable.toString().replace("[^0-9]*".toRegex(), "")
editable.clear()
text.forEach {
editable.append(it)
applyFormat(editable, format)
}
}
}
/**
* Aplica o formato padrão selecionado no texto inserido
*
* @param editable texto inserido na caixa de texto
* @param format formato para aplicar no texto inserido
*
*/
private fun applyFormat(editable: Editable, format: Format) {
val mask = format.toString()
val editableLength = editable.length
if (editableLength < mask.length) {
if (mask[editableLength] != '9') {
editable.append(mask[editableLength])
} else if (mask[editableLength - 1] != '9') {
editable.insert(editableLength - 1, mask, editableLength - 1, editableLength)
} else {
val char = editable.last()
if (mask[editableLength] == '9') {
if (char !in '0'..'9') {
val limit = editable.subSequence(0, editable.length - 1)
editable.clear()
editable.append(limit)
}
}
}
} else if (editableLength > mask.length) {
val limit = editable.subSequence(0, mask.length)
editable.clear()
editable.append(limit)
}
}
/**
* Função para selecionar dinamicamente a máscara conforme o tamanho do texto inserido.
*
* @param editable texto inserido na caixa de texto
* @param formats lista de formatos possiveis para aplicar no texto inserido
*/
private fun getFormat(editable: Editable, formats: Array<out Format>): Format {
var format: Format? = null //formats.first()
formats.sortBy { it.toString().length }
for (it in formats) {
val mask = it.toString()
if (editable.length <= mask.length) {
format = it
break
}
}
return format ?: formats.last()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment