How to 'notice/trap" when user types invalid value when JSpinner loses focus

458 Views Asked by At

This is a follow on SSCCE to my previous question:

Previous Question

This JSpinner background goes RED when users types an invalid value and WHITE when valid. However, if value is invalid and user clicks away from this field the value reverts to whatever was previous.

I want to notice/trap when this happens and inform the user that his typed value is NOT being used, and disable any other functions that rely on this value.

How can I ammend the following code to accomplish that?

public static void main(String[] args) {
    // TODO Auto-generated method stub

    JFrame F = new JFrame();
    F.setVisible(true);
    JPanel p = new JPanel();


    final JSpinner spin2 = new JSpinner();
    spin2.setModel(new SpinnerNumberModel(10, 10, 100, 1));

    JComponent comp = spin2.getEditor();
    JFormattedTextField field = (JFormattedTextField) comp.getComponent(0);
    DefaultFormatter formatter = (DefaultFormatter) field.getFormatter();
    formatter.setCommitsOnValidEdit(true);


        ((JSpinner.DefaultEditor)Position.getEditor()).getTextField().addPropertyChangeListener(new PropertyChangeListener() {

            @Override
            public void propertyChange(PropertyChangeEvent evt) {
                //LOG.info("" + evt);
                if ("editValid".equals(evt.getPropertyName())) {
                    if (Boolean.FALSE.equals(evt.getNewValue())) {
                        SpinnerNumberModel model = (SpinnerNumberModel) Position.getModel();  

                        ((JSpinner.DefaultEditor)Position.getEditor()).getTextField().setBackground(Color.RED);
                        ((JSpinner.DefaultEditor)Position.getEditor()).getTextField().setToolTipText("Amount must be in range [ " + model.getMinimum() + " ... " + model.getMaximum() + " ] for this symbol");

                    }
                    else{
                        ((JSpinner.DefaultEditor)Position.getEditor()).getTextField().setBackground(Color.WHITE);
                    }
                }

            }
        });


    p.add(spin2);   


    F.add(p);
    F.pack();
    F.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);


}
1

There are 1 best solutions below

0
On

If you need the user's input to persist even if he/she enters wrong input then set the focus lost behaviour to PERSIST.

If you need to notify the user for invalid values typed then add a KeyListener to the spinner's text field. That KeyListener will call commitEdit() which in turn will fail if the user's input is invalid, throwing a checked exception that you will catch and notify the user back.

If you need to notify the user for invalid values when the focus is lost then add a FocusListener to the spinner's text field. That FocusListener will call commitEdit() which in turn will fail if the user's input is invalid, throwing a checked exception that you will catch and notify the user back.

Putting them all together:

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Toolkit;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.text.ParseException;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JSpinner.DefaultEditor;
import javax.swing.SpinnerNumberModel;

public class Main {
    public static void warn(final JSpinner spin) {
        final JFormattedTextField jftf = ((DefaultEditor) spin.getEditor()).getTextField();
        try {
            spin.commitEdit(); //Try to commit given value.
            jftf.setBackground(Color.WHITE); //If successfull the revert back to white background.
        }
        catch (final ParseException px) {
            if (!jftf.getBackground().equals(Color.RED)) { //Guard.
                jftf.setBackground(Color.RED); //Error value given.

                //Inform user:
                Toolkit.getDefaultToolkit().beep();
                JOptionPane.showMessageDialog(spin, "Invalid value given in spinner!");
            }
            //else if the background is already red, means we already checked previously,
            //so we do not inform the user again. We inform him only once.
        }
    }

    public static void main(final String[] args) {
        final JSpinner spin = new JSpinner(new SpinnerNumberModel());
        spin.setPreferredSize(new Dimension(150, 30));
        spin.addChangeListener(e -> warn(spin)); //Needed for reverting back to white if the user enters valid input.

        final JFormattedTextField jftf = ((DefaultEditor) spin.getEditor()).getTextField();
        jftf.setFocusLostBehavior(JFormattedTextField.PERSIST);
        jftf.addKeyListener(new KeyAdapter() {
            //Needed to inform user if they type something wrong:
            @Override public void keyReleased(final KeyEvent kevt) { warn(spin); }
        });
        jftf.addFocusListener(new FocusAdapter() {
            //Needed to inform user if the text field loses focus and has a wrong value:
            @Override public void focusLost(final FocusEvent fevt) { warn(spin); }
        });

        final JPanel contents = new JPanel(); //FlowLayout.
        contents.add(new JLabel("Spinning values:", JLabel.CENTER));
        contents.add(spin);

        final JFrame frame = new JFrame("JSpinner mouse");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.getContentPane().add(contents);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }
}