Normally, stateChanged must be called on value set only once. This is what you are accostomed to creating sliders by the conventional JSlider (min, max, val) consrtuctor. However, the handler is evoked twice when creating slider by new JSlider(model) constructor. Looking at the sources
public JSlider(int orientation, int min, int max, int value) {
checkOrientation(orientation);
this.orientation = orientation;
sliderModel = new DefaultBoundedRangeModel(value, 0, min, max);
sliderModel.addChangeListener(changeListener);
updateUI();
}
and the problematic one
public JSlider(BoundedRangeModel brm) {
this.orientation = JSlider.HORIZONTAL;
setModel(brm); // first add
sliderModel.addChangeListener(changeListener); // second add
updateUI();
}
it is worth to mention that the listener is added in the setModel() for the first time.
= = = = = PART 2 = = = = = =
Look at the 3rd demo of tutorial
How to Use Sliders http://java.sun.com/docs/books/tutorial/uiswing/components/slider.html
It creates a formatted text field, which updates slider's value on hitting Enter.
// text field's event handler
public void propertyChange(PropertyChangeEvent e) {
slider.setValue(value.intValue());
}
The slider also updates the edit field onStateChanged forming the feedback loop:
// slider's event handler
public void stateChanged(ChangeEvent e) {
int fps = slider.getValue();
if (!slider.getValueIsAdjusting())
textField.setValue(fps); // we get here editing text in the field
else
textField.setText(fps); // this is useless
Despite the tutorial tells that getValueIsAdjusting() flag serves to break the loop, it is easy to check that the adjusting is false in stateChanged. That is, user hitting Enter changes text field's value, handler of which updates the slider's value, change handler of which changes value (not text) of the field which changes slider's value and so on. The program does not halt just because change events are fired only when new value is different from the old one. In fact what happens is: user enters new value into text field causing slider to accept new value, which envokes slider's changeState() which updates the edit field's value and ... propagation stops since new the incoming value is the one user is being assigend into the edit field.
In addition for the tutorial to be misleading, I consider this approach as fragile. Despite the computer is not an analog machine, who ensures that value traveling through a chain of nodes/handlers (and undergoing type/value conversions herein) will be delivered to the originator unchanged to prevent looping? Suppose a logarithmic scale, where slider accepts log-ed values of the field which are then exponented when returned to the field: slider.setValue(log(base, edit.value)) -> edit.setValue(exp(base, slider.value)) -> slider.setValue( ... -- the sequence should be infinite since any conversion inferes some error. And in this exapmle only two nodes in the chain (edit field and slider) and no type conversions.
The useless for the purpose isAdjusting flag also prevents edit field from updating when user slides until mouse is released. Effectively, user cannot see the current value he selects by slider. Therefore, I prefer a bit more reliable solution: update slider's value with its change handler deteached:
slider.removeChanveListener(cl);
slider.setValue(editFieldValue);
slider.addChanveListener(cl);