How to make the update immediately when the jSpinner value was changed.
ChangeListener listener = new ChangeListener() {
public void stateChanged(ChangeEvent e) {
jLabel.setText(e.getSource());
}
};
spinner1.addChangeListener(listener);
The code above doesnt change the label text automatically, it required you to click again anyplace to update.
The answer is to configure the formatter used in the JFormattedTextField which is a child of the spinner's editor:
formatter.setCommitsOnValidEdit(true);
Unfortunately, getting one's hand on it is as long and dirty as the introductory sentence:
final JSpinner spinner = new JSpinner();
JComponent comp = spinner.getEditor();
JFormattedTextField field = (JFormattedTextField) comp.getComponent(0);
DefaultFormatter formatter = (DefaultFormatter) field.getFormatter();
formatter.setCommitsOnValidEdit(true);
spinner.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
LOG.info("value changed: " + spinner.getValue());
}
});
A slightly (but not by much) cleaner way might be to subclass NumberEditor and expose a method which allows the config
The code you show appears correct. For reference, here is a working example.
Addendum: While the JSpinner
has focus, the left and right arrow keys move the caret. The up arrow increments and the down arrow decrements the field containing the caret. The change is (effectively) simultaneous in both the spinner and the label.
To access the JFormattedTextField
of the JSpinner.DateEditor
, use the parent's getTextField()
method. A suitable caret listener or text input listener may then be used to update the label as desired.
Addendum: Update to use setCommitsOnValidEdit
, as suggested here.
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.util.Calendar;
import java.util.Date;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JSpinner.DateEditor;
import javax.swing.SpinnerDateModel;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.text.DefaultFormatter;
/**
* @see https://stackoverflow.com/questions/2010819
* @see https://stackoverflow.com/questions/3949518
*/
public class JSpinnerTest extends JPanel {
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
JFrame f = new JFrame("JSpinnerTest");
f.add(new JSpinnerTest());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
}
});
}
public JSpinnerTest() {
super(new GridLayout(0, 1));
final JLabel label = new JLabel();
final JSpinner spinner = new JSpinner();
Calendar calendar = Calendar.getInstance();
Date initDate = calendar.getTime();
calendar.add(Calendar.YEAR, -5);
Date earliestDate = calendar.getTime();
calendar.add(Calendar.YEAR, 10);
Date latestDate = calendar.getTime();
spinner.setModel(new SpinnerDateModel(
initDate, earliestDate, latestDate, Calendar.MONTH));
DateEditor editor = new JSpinner.DateEditor(spinner, "MMM yyyy");
spinner.setEditor(editor);
JFormattedTextField jtf = editor.getTextField();
DefaultFormatter formatter = (DefaultFormatter) jtf.getFormatter();
formatter.setCommitsOnValidEdit(true);
spinner.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
JSpinner s = (JSpinner) e.getSource();
label.setText(s.getValue().toString());
}
});
label.setText(initDate.toString());
this.add(spinner);
this.add(label);
}
}
Problem here is that when you edit the JSpinner
value manually by typing from the keyboard, the stateChanged
event is not fired until the focus is lost by the JSpinner
or until Enter key has been pressed.
If you want to upload the value, a KeyListener
is needed which will perform a setValue
in the JSpinner
for each typed key.
I leave an example here for a JSpinner
with a SpinnerNumberModel
:
JSpinner spinner= new JSpinner();
spinner.setModel(new SpinnerNumberModel(0, 0, Integer.MAX_VALUE, 1));
spinner.addChangeListener(new ChangeListener() {
@Override
public void stateChanged(ChangeEvent e) {
jLabel.setText(spinner.getValue());
}
});
final JTextField jtf = ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField();
jtf.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
String text = jtf.getText().replace(",", "");
int oldCaretPos = jtf.getCaretPosition();
try {
Integer newValue = Integer.valueOf(text);
spinner.setValue(newValue);
jtf.setCaretPosition(oldCaretPos);
} catch(NumberFormatException ex) {
//Not a number in text field -> do nothing
}
}
});
It might be an late answer but you may use my approach.
As spuas mentioned above the problem is that stateChanged
event is fired only when focus is lost or Enter key is pressed.
Using KeyListeners is not an good idea as well.
It would be better to use DocumentListener
instead. I modified spuas's example a bit and that's what I got:
JSpinner spinner= new JSpinner();
spinner.setModel(new SpinnerNumberModel(0, 0, Integer.MAX_VALUE, 1));
final JTextField jtf = ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField();
jtf.getDocument().addDocumentListener(new DocumentListener() {
private volatile int value = 0;
@Override
public void removeUpdate(DocumentEvent e) {
showChangedValue(e);
}
@Override
public void insertUpdate(DocumentEvent e) {
showChangedValue(e);
}
@Override
public void changedUpdate(DocumentEvent e) {
showChangedValue(e);
}
private void showChangedValue(DocumentEvent e){
try {
String text = e.getDocument().getText(0, e.getDocument().getLength());
if (text==null || text.isEmpty()) return;
int val = Integer.parseInt(text).getValue();
if (value!=val){
System.out.println(String.format("changed value: %d",val));
value = val;
}
} catch (BadLocationException | NumberFormatException e1) {
//handle if you want
}
}
});
The last answer can be rearranged a little to make it a little more flexible. You can simply use this new MyJSpinner in place of any JSpinner. The biggest change is that you can use this new version with any underlying model of the JSpinner (int, double, byte, etc.)
public class MyJSpinner extends JSpinner{
boolean setvalueinprogress=false;
public MyJSpinner()
{
super();
final JTextField jtf = ((JSpinner.DefaultEditor) getEditor()).getTextField();
jtf.getDocument().addDocumentListener(new DocumentListener() {
@Override
public void removeUpdate(DocumentEvent e) {
showChangedValue(e);
}
@Override
public void insertUpdate(DocumentEvent e) {
showChangedValue(e);
}
@Override
public void changedUpdate(DocumentEvent e) {
showChangedValue(e);
}
private void showChangedValue(DocumentEvent e){
try {
if (!setvalueinprogress)
MyJSpinner.this.commitEdit();
} catch (NumberFormatException | ParseException ex) {
//handle if you want
Exceptions.printStackTrace(ex);
}
}
});
}
@Override
public void setValue(Object value) {
setvalueinprogress=true;
super.setValue(value);
setvalueinprogress=false;
}
}
I'm new so I might be breaking some rules and I might be late. But I found some of the answers a bit confusing so I played around in NetBeans IDE and found that if you right click on the jspinner GUI component placed on your jform and go to events-> change, code will be generated for you.
来源:https://stackoverflow.com/questions/3949382/jspinner-value-change-events