Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting ENTER to work with a JSpinner the way it does with a JTextField

First, to make my job explaining a bit easier, here's some of my code:

JSpinner spin = new JSpinner();
JFormattedTextField text = getTextField(spin);

text.addActionListener(new java.awt.event.ActionListener() {
    public void actionPerformed(java.awt.event.ActionEvent evt) {
            // Do stuff...
    }
});

...

private JFormattedTextField getTextField(JSpinner spinner) {
    JComponent editor = spinner.getEditor();

    if (editor instanceof JSpinner.DefaultEditor) {
        return ((JSpinner.DefaultEditor )editor).getTextField();
    } else {
        System.err.println( "Unexpected editor type: "
                           + spinner.getEditor().getClass()
                           + " isn't a descendant of DefaultEditor" );
        return null;
    }
}

So as you can see, I got that far. And indeed, when I type in a value into the text field component of the spinner (JFormattedTextField), and THEN press ENTER, it works.

What I want now is to be able to have the text field respond to ENTER without having to manually type in a new value (which sorta defeats the purpose of making a spinner out of it). How do I do that?

like image 611
Daddy Warbox Avatar asked Dec 18 '08 23:12

Daddy Warbox


2 Answers

I know this is not the action listener...but maybe this can work for you?


    text.addKeyListener( new KeyAdapter() {
            @Override
            public void keyReleased( final KeyEvent e ) {
                if ( e.getKeyCode() == KeyEvent.VK_ENTER ) {
                    System.out.println( "enter pressed" );
                }
            }
        } );
like image 113
javamonkey79 Avatar answered Sep 18 '22 05:09

javamonkey79


I just ran into an issue relating to this, myself. In my case, I had a JSpinner with a SpinnerNumberModel setup so I could input numerical ID values, and then retrieve them from a remote database. This worked all well and good with a JButton as my trigger for the query, but I wanted to be able to switch focus to the field via Tab, and hit enter, and also be able to change the value by entering an ID with numerical keys, then hit enter.

The one that was giving me the most issues was manually entering the ID with numerical keys, then pressing enter. When I would do this, the query would still happen, but it was querying the previous ID rather than the one I just entered, so I'd have to hit enter again to query the new ID (see code)

((JSpinner.DefaultEditor) minRng.getEditor()).getTextField().addKeyListener(new KeyAdapter() {
    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_ENTER) {
            queryData();
        }
    }
});

and the query method:

private void queryData() {
    int minVal = Integer.parseInt(minRng.getValue().toString());
    queryDataBase(minVal);
}

In order to fix this, all I had to do was force the query to wait to run until the spinner updated its value, which was easily done by using SwingUtilities.invokeLater() to force it to the end of the EDT queue, like so:

((JSpinner.DefaultEditor) minRng.getEditor()).getTextField().addKeyListener(new KeyAdapter() {
    @Override
    public void keyPressed(KeyEvent e) {
        if (e.getKeyCode() == KeyEvent.VK_ENTER) {
            SwingUtilities.invokeLater(new Runnable() {
                @Override
                public void run() {
                    queryData();
                }
            });
        }
    }
});

Thanks to the invokeLater() the query now happens on the new value rather than the previous one, when pressing enter, as the JSpinner has then updated its value to the newly input one.

I'm assuming the reason for this is that the value entered is not officially applied to the JSpinner until you hit enter, which also fires the key listener for the query. The KeyEvent for the custom [query] key listener appears to be the first one in the queue, so it runs its code first, and THEN the KeyEvent for applying the value to the JSpinner from the internal JFormattedTextField, which is why it queries the old value. SwingUtilities.invokeLater() just puts the query itself at the end of the queue, so it allows the other KeyEvent to finish its code before running the query.

Edit:

Another way to achieve this is to request the value directly from the JFormattedTextField rather than the JSpinner, as this should also return the newly entered value since the JFormattedTextField contains the value you typed in, but it hasn't yet been passed to the JSpinner, which appears to be where the issue lies.

private void queryData() {
    JFormattedTextField tf = ((JSpinner.DefaultEditor) minRng.getEditor()).getTextField();
    int minVal = Integer.parseInt(tf.getText());
    queryDataBase(minVal);
}

The only problem with this method is that you then have to catch any NumberFormatExceptions, when parsing the value, where the SwingUtilities.invokeLater() approach allows the spinner model to remove any invalid characters, automatically, and continue with the query.

Anyway, I'm still somewhat inexperienced with Java, so if my understanding of this is incorrect, please let me know in the comments.

like image 22
DGolberg Avatar answered Sep 18 '22 05:09

DGolberg