Questions with mask in Jformattedtextfield

Asked

Viewed 541 times

3

Working with JFormattedTextField, I had two problems for which I found nothing that helps me to solve. The example in this case would be a JFormattedTextField for CPF.

First problem: the mask. When I create the mask and run the program, the formatting is already automatically appearing in the system. I wanted you to show the special characters only when typing the numbers or after 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 enter information that is smaller than the size of my mask (amount of "#") and it erases the entered information. But in addition, I wanted a message to appear to the user that the entered value 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                   
    }
  • Or using Jtextfield + Plaindocument.

  • I’ll take a look at the link to see if I can resolve, warning if I have succeeded, thank you

  • If you think about it, the other question doesn’t solve that much. But this feature is the mask, there is no way to avoid, only if you write a mask manually, which I think is too silly if you have already ready. The second doubt, just validate by the number of typed characters, if form less than 14(11 digits + 3 separators) is because it was not filled in correctly.

  • @You still think it’s duplicate? In my opinion it is just something similar that should have a close solution, but still it would not be duplicate, because the answers there do not fully answer the problem here.

  • @Victorstafusa according to my last comment, analyzing better does not seem to be a duplicate even though it is possible to adapt the other answer to meet this, but this does not qualify it as duplicate.

1 answer

3

Using JFormattedTextField I believe it is more complicated, given that we would have to rewrite a custom mask, but I would like to make a much simpler alternative option, using JTextField and PlainDocument, where it is possible not only to filter digits, but also to add CPF separators at typing time. I used the code as the basis of this answer and of this other, where you can find further explanations about the classes involved.

To demonstrate, I made this component that you inherit 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 size of the current string added to the one just typed will exceed the size of 14 characters, which would be the maximum field size, summing the 11 digits of CPF and the 3 separators.

In the innermost if, I did a validation to add the point as separator, since every 3 digits I would have to add a point, I used (offset+1)%4==0 because the point will appear every 4 positions. As 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 previous logic, I added (offset+1)%12==0 for the hyphen to be added as separator of the final digits of the CPF, which is exactly position 12.

Our field now formats as the user types, but still does not prevent him from typing anything, and is using the !Character.isDigit(str.charAt(0)) that I allow to be added only digits per user input and discard anything other than a digit.

To use, simply instantiate as any text component:

CPFTextField field = new CPFTextField();

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

I made an executable example on Github, if you want to see it working before applying in your code.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.