Trocando máscara de um EditText no Android em tempo de execução

Mascara - CNPJ

Uma das dificuldades encontradas com relação a máscara, é a troca de um campo do tipo EditText em tempo de execução.
Para isso, criei um projeto Android no Eclipse e criei a seguinte classe, a qual é responsável por altear a máscara para o tipo desejado.

package br.com.rezende.mascaras;

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

public abstract class Mask {
    public static String unmask(String s) {
        return s.replaceAll("[.]", "").replaceAll("[-]", "")
                .replaceAll("[/]", "").replaceAll("[(]", "")
                .replaceAll("[)]", "");
    }

    public static TextWatcher insert(final String mask, final EditText ediTxt) {
        return new TextWatcher() {
            boolean isUpdating;
            String old = "";

            public void onTextChanged(CharSequence s, int start, int before,
                    int count) {
                String str = Mask.unmask(s.toString());
                String mascara = "";
                if (isUpdating) {
                    old = str;
                    isUpdating = false;
                    return;
                }
                int i = 0;
                for (char m : mask.toCharArray()) {
                    if (m != '#' && str.length() > old.length()) {
                        mascara += m;
                        continue;
                    }
                    try {
                        mascara += str.charAt(i);
                    } catch (Exception e) {
                        break;
                    }
                    i++;
                }
                isUpdating = true;
                ediTxt.setText(mascara);
                ediTxt.setSelection(mascara.length());
            }

            public void beforeTextChanged(CharSequence s, int start, int count,
                    int after) {
            }

            public void afterTextChanged(Editable s) {
            }
        };
    }
}

O método insert() é responsável por criar um objeto do tipo TextWatcher que contém a máscara desejada, armazene esse objeto em sua Activity para uso posterior, conforme código abaixo:

package br.com.rezende.mascaras;

import android.app.Activity;
import android.os.Bundle;
import android.text.TextWatcher;
import android.widget.EditText;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.RadioGroup.OnCheckedChangeListener;

public class MainActivity extends Activity {
    private TextWatcher cpfMask;
    private TextWatcher cnpjMask;
    private RadioGroup radioGroup;
    private RadioButton rdCNPJ;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final EditText cpf = (EditText) findViewById(R.id.txtCPF);
        // Armazene seus TextWatcher para posterior uso
        cpfMask = Mask.insert("###.###.###-##", cpf);
        cpf.addTextChangedListener(cpfMask);

        cnpjMask = Mask.insert("##.###.###/####-##", cpf);
        rdCNPJ = (RadioButton) findViewById(R.id.rdCNPJ);

        radioGroup = (RadioGroup) findViewById(R.id.radioGroup);
        radioGroup.setOnCheckedChangeListener(new OnCheckedChangeListener() {

            @Override
            public void onCheckedChanged(RadioGroup group, int checkedId) {
                int opcao = radioGroup.getCheckedRadioButtonId();
                if (opcao == rdCNPJ.getId()) {
                    cpf.removeTextChangedListener(cpfMask);
                    cpf.addTextChangedListener(cnpjMask);
                } else {
                    cpf.removeTextChangedListener(cnpjMask);
                    cpf.addTextChangedListener(cpfMask);
                }
            }
        });

    }
}

Observe que nas linhas 36 e 39 é realizado um removeTextChangedListener antes do mesmo adicionar a nova máscara ao campo.

Assim, toda vez que um tipo de documento (CPF ou CNPJ) for escolhido em sua Activity, a máscara de edição é alterada.
Se preferir criar sua xml de tela, segue código.

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical" >

        <EditText
            android:id="@+id/txtCPF"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:inputType="text" >

            <requestFocus />
        </EditText>

        <RadioGroup
            android:id="@+id/radioGroup"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" >

            <RadioButton
                android:id="@+id/rdCPF"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:checked="true"
                android:text="CPF" />

            <RadioButton
                android:id="@+id/rdCNPJ"
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:text="CNPJ" />
        </RadioGroup>
    </LinearLayout>

</RelativeLayout>

Essa tela deverá ficar com o seguinte layout:

CPF

 

Abraços,
André Rezende

Anúncios

10 comentários sobre “Trocando máscara de um EditText no Android em tempo de execução

  1. Parabéns pelo Post.
    Encontrei algo semelhante na internet, no entanto, ninguém explica como retirar a máscara do campo. Espero poder conseguir da forma explicada acima.

    Abraço.

  2. Há um problema aí que é na hora de apagar a máscara vai embora, ficando bem feio.. em casos de telefone, por ex, se você digitou um número a mais e remove e o número final está correto, vai ficar sem a formatação adequada!
    Resolvi essa questão alterando o código acima para este:

    int i = 0;
    if( str.length() > old.length()){
    for (char m : mask.toCharArray()) {
    if (m != ‘#’) {
    mascara += m;
    continue;
    }else{
    try {
    mascara += str.charAt(i);
    } catch (Exception e) {
    break;
    }
    i++;
    }
    }
    }else{
    mascara = s.toString();
    }

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

    public abstract class Mask {
    public static String CPF_MASK = “###.###.###-##”;
    public static String CELULAR_MASK = “(##) #### #####”;
    public static String CEP_MASK = “#####-###”;

    public static String unmask(String s) {
    return s.replaceAll(“[.]”, “”).replaceAll(“[-]”, “”)
    .replaceAll(“[/]”, “”).replaceAll(“[(]”, “”)
    .replaceAll(“[)]”, “”).replaceAll(” “, “”)
    .replaceAll(“,”, “”);
    }

    public static boolean isASign(char c) {
    if (c == ‘.’ || c == ‘-‘ || c == ‘/’ || c == ‘(‘ || c == ‘)’ || c == ‘,’ || c == ‘ ‘) {
    return true;
    } else {
    return false;
    }
    }

    public static String mask(String mask, String text) {
    int i = 0;
    String mascara = “”;
    for (char m : mask.toCharArray()) {
    if (m != ‘#’) {
    mascara += m;
    continue;
    }
    try {
    mascara += text.charAt(i);
    } catch (Exception e) {
    break;
    }
    i++;
    }

    return mascara;
    }

    public static TextWatcher insert(final String mask, final EditText ediTxt) {
    return new TextWatcher() {
    boolean isUpdating;
    String old = “”;

    public void onTextChanged(CharSequence s, int start, int before, int count) {
    String str = Mask.unmask(s.toString());
    String mascara = “”;
    if (isUpdating) {
    old = str;
    isUpdating = false;
    return;
    }

    int index = 0;
    for (int i = 0; i < mask.length(); i++) {
    char m = mask.charAt(i);
    if (m != '#') {
    if (index == str.length() && str.length() 0) {
    char last_char = mascara.charAt(mascara.length() – 1);
    boolean hadSign = false;
    while (isASign(last_char) && str.length() == old.length()) {
    mascara = mascara.substring(0, mascara.length() – 1);
    last_char = mascara.charAt(mascara.length() – 1);
    hadSign = true;
    }

    if (mascara.length() > 0 && hadSign) {
    mascara = mascara.substring(0, mascara.length() – 1);
    }
    }

    isUpdating = true;
    ediTxt.setText(mascara);
    ediTxt.setSelection(mascara.length());
    }

    public void beforeTextChanged(CharSequence s, int start, int count, int after) {}

    public void afterTextChanged(Editable s) {}
    };
    }
    }

    Ae ta o codigo da classe Mask. Com a diferença que ele vai funcionar tbm enquanto estiver deletando os characters. Levei quase 2 dias para implementar essa função adequadamente. Ta bem testada jah.

    Use essa classe assim:

    EditedText etCPF = /* sua inicialização aqui */;
    etCPF.addTextChangedListener(Mask.insert(Mask.CPF_MASK, etCPF));

  4. Gostei Muito , resolveu meu problema

  5. André, boa tarde.

    Primeiramente, parabéns pelo trabalho. Ficou excelente e me é muito útil. Em segundo lugar, detectei um pequeno problema e fiz uma alteração. Quando o usuário deleta um char, o cursor vai automaticamente para o fim. Reparei em testes de producao que muitas vezes as pessoas nem percebem que o cursor foi para o fim, e, quando desejam apagar mais de um numero, acabam errando e nao percebem. Além disso, inclui espaços como char possível para a máscara também. Compartilho aqui abaixo o código, mas gostaria de sugerir que voce o colocasse no github, para que todos possam contribuit, (como muitos estao fazendo nos comentarios acima). Além disso, seria bem legal para o seu currículo também. Abraços!

    public abstract class Mask {

    public static String unmask(String s) {
    return s.replaceAll(“[.]”, “”).replaceAll(“[-]”, “”)
    .replaceAll(“[/]”, “”).replaceAll(“[(]”, “”)
    .replaceAll(“[)]”, “”).replaceAll(” “, “”);
    }

    public static TextWatcher insert(final String mask, final EditText ediTxt) {
    return new TextWatcher() {
    boolean isUpdating = false;
    int selection = 0;
    int beforeTextSize;
    String old = “”;

    public void onTextChanged(CharSequence s, int start, int before,
    int count) {
    String str = Mask.unmask(s.toString());
    String mascara = “”;
    if (isUpdating) {
    old = str;
    isUpdating = false;
    return;
    }
    int i = 0;
    for (char m : mask.toCharArray()) {
    if (m != ‘#’ && str.length() > old.length()) {
    mascara += m;
    continue;
    }
    try {
    mascara += str.charAt(i);
    } catch (Exception e) {
    break;
    }
    i++;
    }
    isUpdating = true;
    if (str.length() >= old.length()) {
    ediTxt.setText(mascara);
    // selection += selection < mask.length() &&
    // mask.charAt(selection) != '#' ? count + 1 : count;
    selection += selection + 1 < mask.length() &&
    (mask.charAt(selection) != '#' || mask.charAt(selection+1) != '#') ? count + 1 : count;
    ediTxt.setSelection(selection <= mascara.length() ? selection : mascara.length());
    }
    }

    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
    if (!isUpdating) {
    selection = ediTxt.getSelectionStart();
    Log.d(TAG, "beforeTextChanged: " + selection);
    }
    }

    public void afterTextChanged(Editable s) {
    }
    };
    }

    }

  6. Boa sugestão, devo logo colocar no github já com as correções.

  7. Para resolver o problema da falta de formatação quando se está deletando a string, basta adicionar o pequeno code abaixo da linha 22, antes do if(isUpdating){ :

    if (count == 0) //is deliting
    isUpdating = true;

    • Parabéns pelo exemplo.
      Uma outra atualização é que também está permitindo espaçoes na string. É só adicionar o espaço na relação de strings não permitidas na function unmask(…)

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s