Search code examples
javaswingdateformatterjformattedtextfield

JFormattedTextField with initial value which can be overwritten sign by sign


I have such easy code which creates JFormattedTextField which should contain a birth date (only digits divided by hyphen):

MaskFormatter mf = new MaskFormatter("##-##-####");
mf.setPlaceholderCharacter('_');
JFormattedTextField birthDate = new JFormattedTextField(mf);

But how to create birthDate in order it would contain initial value "dd-mm-yyyy".
I know there is such thing like setPlaceholder("some string") but when i use it, only way to change walue of birthDate is to select whole existied content and then typed in new one.
My intention is to allow user to overwrite existing content sign by sign and if after lost of focus this value is not allowed, back to initial one. Eg.

1d-mm-yyyy
11-mm-yyyy
11-0m-yyyy (now birthDate is loosing focus)
dd-mm-yyyy (because value is wrong birthDate is as on the beginning)

Is such behaviour of JFormattedTextField even possible? Or maybe there is some more suitable JComponent for this?


Solution

  • not an answer just the direction, to use some of JCalendar

    simple code example for JSpinner (I'm can't found notifier for PropertyChangeListener/Support)

    import java.awt.*;
    import java.text.SimpleDateFormat;
    import java.util.*;
    import javax.swing.*;
    import javax.swing.event.*;
    
    public class TimeZoneSpinners {
    
        private final String[] zones = {"Asia/Tokyo", "Asia/Hong_Kong",
            "Asia/Calcutta", "Europe/Paris", "Europe/London",
            "America/New_York", "America/Los_Angeles"
        };
        private final JLabel[] labels = new JLabel[zones.length];
        private final SimpleDateFormat[] formats = new SimpleDateFormat[zones.length];
        private JSpinner spinner;
        private SpinnerDateModel model;
        private SimpleDateFormat format;
        private JPanel panel;
        private JFrame frame = new JFrame();
    
        public TimeZoneSpinners() {
            Calendar cal = Calendar.getInstance();
            Date date = cal.getTime();
            model = new SpinnerDateModel();
            model.setValue(date);
            spinner = new JSpinner(model);
            spinner.addChangeListener(new ChangeListener() {
                @Override
                public void stateChanged(ChangeEvent e) {
                    Date date = (Date) ((JSpinner) e.getSource()).getValue();
                    for (int i = 0; i < labels.length; i++) {
                        labels[i].setText(formats[i].format(date));
                    }
                }
            });
            format = ((JSpinner.DateEditor) spinner.getEditor()).getFormat();
            format.setTimeZone(TimeZone.getTimeZone(zones[0]));
            format.applyPattern("yyyy-MM-dd HH:mm:ss");
            format.applyPattern("HH:mm:ss");
            panel = new JPanel(new GridLayout(zones.length, 2, 10, 10));
            for (int i = 0; i < zones.length; i++) {
                formats[i] = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss");
                formats[i] = new SimpleDateFormat("HH:mm:ss");
                formats[i].setTimeZone(TimeZone.getTimeZone(zones[i]));
                JLabel label = new JLabel(zones[i]);
                labels[i] = new JLabel(formats[i].format(date));
                labels[i].setHorizontalAlignment(JLabel.RIGHT);
                panel.add(label);
                panel.add(labels[i]);
            }
            frame.setLayout(new BorderLayout(10, 10));
            frame.add(spinner, BorderLayout.NORTH);
            frame.add(panel, BorderLayout.CENTER);
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.pack();
            frame.setVisible(true);
        }
    
        public static void main(String[] args) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    new TimeZoneSpinners();
                }
            });
        }
    }
    

    you should be able to override AbstractFormatter/InternationalFormatter (example for number instance only) or to override those methods for JFormattedTextField / JSpinner, and with input mast and verifier e.g.

    import javax.swing.*;
    import javax.swing.text.DefaultFormatter;
    import javax.swing.text.InternationalFormatter;
    import java.text.DateFormat;
    import java.text.Format;
    import java.util.Calendar;
    import java.util.Date;
    import java.util.TimeZone;
    
    public class DateSpinner extends JSpinner {
    
        private static final long serialVersionUID = 1L;
        public DefaultFormatter _formatter;
        public DateEditor _timeEditor;
        public DateFormat _format;
    
        public DateSpinner() {
            this("hh:mm:ss");
        }
    
        public DateSpinner(String format) {
            this(format, new Date());
        }
    
        public DateSpinner(String format, Date date) {
            super(new SpinnerDateModel(date, null, null, Calendar.HOUR_OF_DAY));
            setFormat(format);
            customizeSpinner();
        }
    
        private void customizeDateEditor() {
            JFormattedTextField.AbstractFormatter formatter = _timeEditor.getTextField().getFormatter();
            if (formatter instanceof DefaultFormatter) {
                _formatter = (DefaultFormatter) formatter;
            } else {
                throw new IllegalStateException("The formatter is not an instance of DefaultFormatter.");
            }
            if (formatter instanceof InternationalFormatter) {
                Format f = ((InternationalFormatter) formatter).getFormat();
                if (f instanceof DateFormat) {
                    _format = ((DateFormat) f);
                }
            }
            if (_format == null) {
                throw new IllegalStateException("The format is not an instance of SimpleDateFormat.");
            }
        }
    
        private void setFormat(String format) {
            _timeEditor = createDateEditor(format);
            customizeDateEditor();
            setEditor(_timeEditor);
        }
    
        private void customizeSpinner() {
            setLenient(false);
            setCommitsOnValidEdit(true);
            setAllowsInvalid(false);
            setOverwriteMode(true);
        }
    
        protected DateEditor createDateEditor(String format) {
            return new DateEditor(this, format);
        }
    
        public void setCommitsOnValidEdit(boolean commit) {
            _formatter.setCommitsOnValidEdit(commit);
        }
    
        public boolean getCommitsOnValidEdit() {
            return _formatter.getCommitsOnValidEdit();
        }
    
        public void setOverwriteMode(boolean overwriteMode) {
            _formatter.setOverwriteMode(overwriteMode);
        }
    
        public boolean getOverwriteMode() {
            return _formatter.getOverwriteMode();
        }
    
        public void setAllowsInvalid(boolean allowsInvalid) {
            _formatter.setAllowsInvalid(allowsInvalid);
        }
    
        public boolean getAllowsInvalid() {
            return _formatter.getAllowsInvalid();
        }
    
        public void setTimeZone(TimeZone zone) {
            _format.setTimeZone(zone);
        }
    
        public TimeZone getTimeZone() {
            return _format.getTimeZone();
        }
    
        public void setLenient(boolean lenient) {
            _format.setLenient(lenient);
        }
    
        public boolean isLenient() {
            return _format.isLenient();
        }
    }