0

I Created a JFormattedTextField and using as a javaBean for multiple java Project, however, i didn't manage to figure out how use this field with a mask, the mask just doesn't work

Here's my class:

    package org.japo.java.swing.samples;

import java.text.ParseException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFormattedTextField;
import javax.swing.text.MaskFormatter;

/**
 *
 * @author ambro
 */
public class TextFieldCPF extends JFormattedTextField {

    public TextFieldCPF() {
    }
    
    public TextFieldCPF (String string){
        TextFieldCPF FormatoCPF = new TextFieldCPF("");
        try {
            MaskFormatter mask = new MaskFormatter("###");
            mask.install(FormatoCPF);
        } catch (ParseException ex) {
            Logger.getLogger(TextFieldCPF.class.getName()).log(Level.SEVERE, null, ex);
        }
        
    }
}

I also tried to use a mask in this way:

try {
        MaskFormatter mask = new MaskFormatter("###");
        FormatoCPF = new JFormattedTextField(mask);
    } catch (ParseException ex) {
        Logger.getLogger(TextFieldCPF.class.getName()).log(Level.SEVERE, null, ex);
    }

So, how to mask a JFormattedTextField?

To improve what my question is:

I have this simple frame:

public class TestFrame extends javax.swing.JFrame {

public TestFrame() {
    initComponents();
    
}

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

    textFieldCPF1 = new org.japo.java.swing.samples.TextFieldCPF();

    setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);

    javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
    getContentPane().setLayout(layout);
    layout.setHorizontalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(layout.createSequentialGroup()
            .addGap(117, 117, 117)
            .addComponent(textFieldCPF1, javax.swing.GroupLayout.PREFERRED_SIZE, 97, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addContainerGap(137, Short.MAX_VALUE))
    );
    layout.setVerticalGroup(
        layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
        .addGroup(layout.createSequentialGroup()
            .addGap(79, 79, 79)
            .addComponent(textFieldCPF1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
            .addContainerGap(90, Short.MAX_VALUE))
    );

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

/**
 * @param args the command line arguments
 */
public static void main(String args[]) {
    /* Set the Nimbus look and feel */
    //<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
    /* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
     * For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html 
     */
    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(TestFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (InstantiationException ex) {
        java.util.logging.Logger.getLogger(TestFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (IllegalAccessException ex) {
        java.util.logging.Logger.getLogger(TestFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    } catch (javax.swing.UnsupportedLookAndFeelException ex) {
        java.util.logging.Logger.getLogger(TestFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
    }
    //</editor-fold>
    //</editor-fold>

    /* Create and display the form */
    java.awt.EventQueue.invokeLater(new Runnable() {
        public void run() {
            new TestFrame().setVisible(true);
            
        }
    });
}

// Variables declaration - do not modify                     
private org.japo.java.swing.samples.TextFieldCPF textFieldCPF1;
// End of variables declaration                   

}

What I want is to use the class TextFieldCPF in multiple java projects, not only in TestFrame

TextFieldCPF will have more than just the mask, afterwards i'll try to set other features and this field will be use as the pattern for other projects.

  • `public TextFieldCPF (String string){` is wrong, don't create another instance of `TextFieldCPF` inside the constructor, use the current instance (`this`) instead, also, throw the exception to the caller – MadProgrammer Apr 26 '22 at 21:25

1 Answers1

0

Your constructor public TextFieldCPF (String string){ is wrong. Don't create a new instance of the TextFieldCPF within the constructor, instead make use of the instance of the class which is been created (ie this). Also, you shouldn't be consuming the exception, the caller needs to know when this fails.

public TextFieldCPF(String string) throws ParseException {
    MaskFormatter mask = new MaskFormatter(string);
    mask.install(this);
}

Having said that, the constructor is ambiguous, what is string? Is it the text of field? Some kind of prompt? A tooltip?

Instead, you should make the parameter meaningful, in this case, ask for an instance of MaskFormatter, for example...

public TextFieldCPF(MaskFormatter mask) {
    mask.install(this);
}

or, alternatively, provide a factory method...

public static TextFieldCPF withMask(String mask) throws ParseException {
    TextFieldCPF field = new TextFieldCPF();
    MaskFormatter formatter = new MaskFormatter(mask);
    formatter.install(field);
    return field;
}

then code becomes more self documenting.

Talking of factories, since you're not actually adding any new functionality to the class, you should prefer composition over inheritance, instead, create and actual an factory class, for example...

public static class FormattedTextFieldFactory {
    public static JFormattedTextField withMask(String mask) throws ParseException {
        JFormattedTextField field = new JFormattedTextField();
        MaskFormatter formatter = new MaskFormatter(mask);
        formatter.install(field);
        return field;
    }
}

So, when I try to use public TextFieldCPF(MaskFormatter mask) { mask.install(this); } I get this error: Exception in thread "AWT-EventQueue-0" java.lang.RuntimeException: Uncompilable source code - constructor TextFieldCPF in class org.japo.java.swing.samples.TextFieldCPF cannot be applied to given types; required: javax.swing.text.MaskFormatter found: no arguments reason: actual and formal argument lists differ in length

That's because you've not acutally implemented the public TextFieldCPF(MaskFormatter mask) { constructor - the error message is telling you every thing you need to know, for example...

import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.text.ParseException;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.text.MaskFormatter;

public final class Main {
    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    JFrame frame = new JFrame();
                    frame.add(new TestPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                } catch (ParseException ex) {
                    ex.printStackTrace();;
                }
            }
        });
    }

    public class TextFieldCPF extends JFormattedTextField {
        public TextFieldCPF(String string) throws ParseException {
            MaskFormatter mask = new MaskFormatter(string);
            mask.install(this);
            setColumns(string.length());
        }
    }

    public class TestPane extends JPanel {
        public TestPane() throws ParseException {
            setLayout(new GridBagLayout());
            add(new TextFieldCPF("###"));
        }
    }
}

When I try public static TextFieldCPF withMask(String mask) throws ParseException { TextFieldCPF field = new TextFieldCPF(); MaskFormatter formatter = new MaskFormatter("###"); formatter.install(field); return field; } The mask does not work, just gives me a normal textfield

Works fine for me...

import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.text.ParseException;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.text.MaskFormatter;

public final class Main {
    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    JFrame frame = new JFrame();
                    frame.add(new TestPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                } catch (ParseException ex) {
                    ex.printStackTrace();;
                }
            }
        });
    }

    public static class TextFieldCPF extends JFormattedTextField {
        public TextFieldCPF() {
        }

        public static TextFieldCPF withMask(String mask) throws ParseException {
            TextFieldCPF field = new TextFieldCPF();
            MaskFormatter formatter = new MaskFormatter(mask);
            formatter.install(field);
            field.setColumns(mask.length());
            return field;
        }
    }

    public class TestPane extends JPanel {
        public TestPane() throws ParseException {
            setLayout(new GridBagLayout());
            add(TextFieldCPF.withMask("###"));
        }
    }
}

And just to be sure, so does the factory...

import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.text.ParseException;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.text.MaskFormatter;

public final class Main {
    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    JFrame frame = new JFrame();
                    frame.add(new TestPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                } catch (ParseException ex) {
                    ex.printStackTrace();;
                }
            }
        });
    }

    public static class FormattedTextFieldFactory {
        public static JFormattedTextField withMask(String mask) throws ParseException {
            JFormattedTextField field = new JFormattedTextField();
            field.setColumns(mask.length());
            MaskFormatter formatter = new MaskFormatter(mask);
            formatter.install(field);
            return field;
        }
    }

    public class TestPane extends JPanel {
        public TestPane() throws ParseException {
            setLayout(new GridBagLayout());
            add(FormattedTextFieldFactory.withMask("###"));
        }
    }
}

What I'm searching for is how to build a class to use in multiple projects Make a pattern of a JFormattedTextField with a mask, size and other features

My point remains the same, prefer composition over inheritance, you're not actually adding new functionality to the class, you're just "configuring" it, which might make a builder pattern more useful, for example...

import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.text.ParseException;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.text.MaskFormatter;

public final class Main {
    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    JFrame frame = new JFrame();
                    frame.add(new TestPane());
                    frame.pack();
                    frame.setLocationRelativeTo(null);
                    frame.setVisible(true);
                } catch (ParseException ex) {
                    ex.printStackTrace();;
                }
            }
        });
    }
    
    public class FormattedTextFieldBuilder {
        private JFormattedTextField textField;
        
        private String maskValue;
        private int columnCount = -1;
        private Object value;

        public FormattedTextFieldBuilder(JFormattedTextField textField) {
            this.textField = textField;
        }

        public FormattedTextFieldBuilder() {
            this(new JFormattedTextField());
        }
        
        public FormattedTextFieldBuilder withMask(String mask) {
            maskValue = mask;
            return this;
        }
        
        public FormattedTextFieldBuilder withColumnCount(int columnCount) throws ParseException {
            this.columnCount = columnCount > 0 ? columnCount : -1;
            return this;
        }
        
        public FormattedTextFieldBuilder withValue(Object value) throws ParseException {
            this.value = value;
            return this;
        }

        protected JFormattedTextField getTextField() {
            return textField;
        }

        protected String getMaskValue() {
            return maskValue;
        }
        
        protected MaskFormatter getMask() throws ParseException {
            String maskValue = getMaskValue();
            if (maskValue == null) {
                return null;
            }
            return new MaskFormatter(maskValue);

        }

        protected int getColumnCount() {
            return columnCount;
        }

        protected Object getValue() {
            return value;
        }
        
        public JFormattedTextField build() throws ParseException {
            JFormattedTextField textField = getTextField();
            MaskFormatter mask = getMask();
            int columnCount = getColumnCount();
            Object value = getValue();
            if (mask != null) {
                mask.install(textField);
            }
            if (columnCount > -1) {
                textField.setColumns(columnCount);
            }
            if (value != null) {
                textField.setValue(value);
            }
            return textField;
        }
    }

    public class TestPane extends JPanel {
        public TestPane() throws ParseException {
            setLayout(new GridBagLayout());
            
            JFormattedTextField textField = new FormattedTextFieldBuilder()
                    .withColumnCount(3)
                    .withMask("###")
                    .build();
            
            add(textField);
        }
    }
}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • So, when I try to use ` public TextFieldCPF(MaskFormatter mask) { mask.install(this); }` I get this error: `Exception in thread "AWT-EventQueue-0" java.lang.RuntimeException: Uncompilable source code - constructor TextFieldCPF in class org.japo.java.swing.samples.TextFieldCPF cannot be applied to given types; required: javax.swing.text.MaskFormatter found: no arguments reason: actual and formal argument lists differ in length` – Luís Felipe Ambrozin Apr 27 '22 at 19:26
  • When I try `public static TextFieldCPF withMask(String mask) throws ParseException { TextFieldCPF field = new TextFieldCPF(); MaskFormatter formatter = new MaskFormatter("###"); formatter.install(field); return field; }` The mask does not work, just gives me a normal textfield – Luís Felipe Ambrozin Apr 27 '22 at 19:26
  • What I'm searching for is how to build a class to use in multiple projects Make a pattern of a JFormattedTextField with a mask, size and other features – Luís Felipe Ambrozin Apr 27 '22 at 19:31
  • `constructor TextFieldCPF in class org.japo.java.swing.samples.TextFieldCPF cannot be applied to given types; required: javax.swing.text.MaskFormatter found: no arguments reason: actual and formal argument lists differ in length` <- is telling you ever thing you need - you've not created a public constructor which accepts a `MaskFormatter` – MadProgrammer Apr 27 '22 at 22:00
  • First, there were a couple of typos in the code (using `###` instead of mask), now fixed. Second, when I `FormattedTextFieldFactory.withMask` it works fine for me – MadProgrammer Apr 27 '22 at 22:11
  • When i code the `JFormattedTextField` in the same project works fine, the problem I have is when i use in differents projects. For example, when i include in the palette and try to use in another project, just gives me a commom field – Luís Felipe Ambrozin Apr 28 '22 at 19:48
  • @LuísFelipeAmbrozin 1. Don't use it in the palette, make the effort to learn how to make your UIs by hand, seriously, it will be much simpler. 2. You need to learn what a Jar library is and your "common" code will need to be included in that library(s) and then that library will need to be added to your other project dependencies – MadProgrammer Apr 28 '22 at 21:25
  • I think with your tips i'm able to create a GUI by hand, however I would like to learn how to reuse directly from the palette, even with there's nothing new and only configuration of textfield, if you can recommend a tutorial i would really appreciate (sorry with i'm being annoying i'm just a begginer in java trying to understand why not reuse even if it's something really simple) – Luís Felipe Ambrozin May 02 '22 at 16:45
  • And I know that I could only use **ctrl + C** , **ctrl + V** in multiple projects, however, with I need to change a simples mask from multiple projects, a lot more time is going to be spend afterwards to change a simple mask, with I'm able to just change in one class and then, more than one project is with my new modification probably is going to be faster while i'm coding. – Luís Felipe Ambrozin May 02 '22 at 16:50
  • *"however I would like to learn how to reuse directly from the palette"* - So, what I would do, is make use of the `JFormattedTextField` which already resides in the palette and then use the "configuration utility" to configure it - but this is based on 22 years of experience using Netbeans – MadProgrammer May 02 '22 at 22:00
  • *"a lot more time is going to be spend afterwards to change a simple mask, with I'm able to just change in one class and then, more than one project is with my new modification probably is going to be faster while i'm coding"* - IMHO, that's probably not the right approach to the problem, as this could have adverse effects on projects which don't want the mask to change - which is why I always fall to wanting to make use composition. The form editor has the side effect of encouraging bad development practices, which is why I generally recommend against using it (IMHO) – MadProgrammer May 02 '22 at 22:06
  • My first answer will fix your overall problem - the fact that it's Netbeans is falling back to a plain `JTextField` will be more related to how you're setting up Netbeans then my code, as using it without the palette/editor works just fine – MadProgrammer May 02 '22 at 22:07