3

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);


}
Community
  • 1
  • 1
ManInMoon
  • 6,795
  • 15
  • 70
  • 133
  • Me probably being dense, but still don't understand: the invalid value was never committed (doesn't exist), so there _can't_ be anything that relies on it .. The user knows (by its appearance) that it is invalid, you can inform him/her once that this means "not committed, will be reverted to previous" (f.i. by a tooltip/statusbar message), then when s/he moves away the expected/announced will happen. No reason - that I see - to tell again - except you have some additional requirement you didn't tell us :-) – kleopatra Jan 07 '14 at 10:49
  • I am not telling the user anything - yet. He just sees that background is RED (obviously that would tell most people that there was an issue). However, if he chooses to leave it invalid/RED I want my code to notice when he moves away from this spinner. – ManInMoon Jan 07 '14 at 12:08
  • @ManInMoon ... I had a go at this earlier in the week. If the value entered is outside the range, the `spinner.getValue()` remains whatever the last valid value was. So your calculations will be whatever they were after that last (valide) entry. As far as I can tell, the spinner ONLY gets updated when the user pressed `ENTER`. That said the underlying JTextField _holds on to_ the new and incorrect value. Probably for display purposes ... I wanted the display to reflect the _actual value_ of the model; but that didn't seem to work either. :-( – will Aug 23 '17 at 01:58

1 Answers1

2

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);
    }
}
gthanop
  • 3,035
  • 2
  • 10
  • 27