Doubts with mask in JFormattedTextField

3

Working with JFormattedTextField , I got two problems that I could not find anything to help me solve. The example in this case would be JFormattedTextField for CPF.

First problem: the mask. When I create the mask and run the program, the formatting is automatically appearing on the system. I wanted you to only display the special characters when typing or typing.

try {
        txtCPF.setFormatterFactory(new javax.swing.text.DefaultFormatterFactory(new javax.swing.text.MaskFormatter("###.###.###-##")));
    } catch (java.text.ParseException ex) {
        ex.printStackTrace();
    }

As you can see in the code, all "." and "-" already appear at the beginning, before you can change anything.

Second problem: warning. When I type information that is smaller than the size of my mask ("#"), it deletes the information entered. But in addition, I wanted a message to appear to the user that the value entered was invalid.

Below is an example for testing and analyzing problems:

package telas;

public class amostra extends javax.swing.JFrame {

    public amostra() {
        initComponents();
    }

    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
    private void initComponents() {

        jFormattedTextField1 = new javax.swing.JFormattedTextField();
        jLabel1 = new javax.swing.JLabel();
        jTextField1 = new javax.swing.JTextField();
        jLabel2 = new javax.swing.JLabel();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

        try {
            jFormattedTextField1.setFormatterFactory(new javax.swing.text.DefaultFormatterFactory(new javax.swing.text.MaskFormatter("###.###.###-##")));
        } catch (java.text.ParseException ex) {
            ex.printStackTrace();
        }

        jLabel1.setText("cpf");

        jLabel2.setText("Outro campo:");

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
        getContentPane().setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
                .addContainerGap(69, Short.MAX_VALUE)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addComponent(jLabel1, javax.swing.GroupLayout.Alignment.TRAILING)
                    .addComponent(jLabel2, javax.swing.GroupLayout.Alignment.TRAILING))
                .addGap(18, 18, 18)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
                    .addComponent(jTextField1)
                    .addComponent(jFormattedTextField1, javax.swing.GroupLayout.DEFAULT_SIZE, 121, Short.MAX_VALUE))
                .addGap(126, 126, 126))
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(layout.createSequentialGroup()
                .addGap(101, 101, 101)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jFormattedTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel1))
                .addGap(18, 18, 18)
                .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
                    .addComponent(jTextField1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                    .addComponent(jLabel2))
                .addContainerGap(141, Short.MAX_VALUE))
        );

        pack();
    }// </editor-fold>                        

    public static void main(String args[]) {

        try {
            for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
                if ("Nimbus".equals(info.getName())) {
                    javax.swing.UIManager.setLookAndFeel(info.getClassName());
                    break;
                }
            }
        } catch (ClassNotFoundException ex) {
            java.util.logging.Logger.getLogger(amostra.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (InstantiationException ex) {
            java.util.logging.Logger.getLogger(amostra.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (IllegalAccessException ex) {
            java.util.logging.Logger.getLogger(amostra.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        } catch (javax.swing.UnsupportedLookAndFeelException ex) {
            java.util.logging.Logger.getLogger(amostra.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
        }



        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                new amostra().setVisible(true);
            }
        });
    }

    // Variables declaration - do not modify                     
    private javax.swing.JFormattedTextField jFormattedTextField1;
    private javax.swing.JLabel jLabel1;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JTextField jTextField1;
    // End of variables declaration                   
    }
    
asked by anonymous 14.08.2017 / 15:55

1 answer

4

Using JFormattedTextField , I think it is more complicated, since we would have to rewrite a custom masquerade, but I would like to leave an alternative option much simpler, using JTextField and PlainDocument , where it is possible not only to filter only digits, but also add the CPF separators at typing time. I used the this response code and of this other , where you can find further explanations of the classes involved.

To demonstrate, I made this component that inherits from JTextField :

class CPFTextField extends JTextField {

    public CPFTextField() {

        setDocument(new PlainDocument() {

            @Override
            public void insertString(int offset, String str, AttributeSet attr) throws BadLocationException {

                if (str == null || !Character.isDigit(str.charAt(0))) {
                    return;
                }

                if ((getLength() + str.length()) <= 14) {

                    if (offset > 0 && offset < 9 && (offset + 1) % 4 == 0) {
                        str = "." + str;
                    } else if (offset > 0 && (offset + 1) % 12 == 0) {
                        str = "-" + str;
                    }
                    super.insertString(offset, str, attr);
                }
            }
        });
    }
}

In condition ((getLength() + str.length()) <= 14) I check if the current string size added to the one just typed will exceed the length of 14 characters, which would be the maximum field size, adding the 11 CPF digits and the 3 tabs.

In the innermost if, I made a validation to add the point as separator, considering that every 3 digits I would have to add a point, I used (offset+1)%4==0 because the point will appear every 4 positions. Since we have 14 positions, I had to add offset < 9 because the point will only be added 2 times (in the fourth and eighth position).

Following the same logic as above, I added (offset+1)%12==0 so that the hyphen was added as a separator of the final digits of the CPF, which is exactly position 12.

Our field now formats as the user types, but it still does not stop you from typing anything, and it is using !Character.isDigit(str.charAt(0)) that I allow only digits per user input to be added and I discard anything else typed that is not one digit.

To use, just instantiate as a text component any:

CPFTextField field = new CPFTextField();

and add on your screen, as it remains a JTextField , despite what we've customized in the class. To validate, just check that the text of the component is equal to 14, if it is not, it was not completed correctly.

I made an executable example in the GitHub if you want to see it working before you apply it to your code.

    
14.08.2017 / 17:43