Question

I have wee bit of code that updates a JSlider; and at another point in time, the JSlider's maximum value needs to updated. The problem is when I call setMaximum() on the slider, it also dispatches a ChangeEvent. To avoid that I'm doing this:

slider.removeChangeListener(this);
slider.setMaximum(newMax);
slider.addChangeListener(this);

Is there a cleaner/more elegant way of doing this?

Was it helpful?

Solution

A clean way might be (depending a bit on what you actually need) to implement a custom BoundedRangeModel which fires a custom ChangeEvent that can carry the actually changed properties:

/**
 * Extended model that passes the list of actually changed properties
 * in an extended changeEvent.
 */
public static class MyBoundedRangeModel extends DefaultBoundedRangeModel {

    public MyBoundedRangeModel() {
    }

    public MyBoundedRangeModel(int value, int extent, int min, int max) {
        super(value, extent, min, max);
    }

    @Override
    public void setRangeProperties(int newValue, int newExtent, int newMin,
            int newMax, boolean adjusting) {
        int oldMax = getMaximum();
        int oldMin = getMinimum();
        int oldValue = getValue();
        int oldExtent = getExtent();
        boolean oldAdjusting = getValueIsAdjusting();
        // todo: enforce constraints of new values for all
        List<String> changedProperties = new ArrayList<>();
        if (oldMax != newMax) {
           changedProperties.add("maximum"); 
        }
        if (oldValue != newValue) {
            changedProperties.add("value");
        }
        // todo: check and add other properties 
        changeEvent = changedProperties.size() > 0 ? 
                new MyChangeEvent(this, changedProperties) : null;
        super.setRangeProperties(newValue, newExtent, newMin, newMax, adjusting);
    }


}

/**
 * Extended ChangeEvent that provides a list of actually 
 * changed properties. 
 */
public static class MyChangeEvent extends ChangeEvent {

    private List<String> changedProperties;

    /**
     * @param source
     */
    public MyChangeEvent(Object source, List<String> changedProperties) {
        super(source);
        this.changedProperties = changedProperties;
    }

    public List<String> getChangedProperties() {
        return changedProperties;
    }

}

Its usage something like:

final JSlider slider = new JSlider();
slider.setModel(new MyBoundedRangeModel(0, 0, -100, 100));
ChangeListener l = new ChangeListener() {

    @Override
    public void stateChanged(ChangeEvent e) {
        if (e instanceof MyChangeEvent) {
            MyChangeEvent me = (MyChangeEvent) e;
            if (me.getChangedProperties().contains("value")) {
               System.out.println("new value: " + 
                    ((BoundedRangeModel) e.getSource()).getValue()); 
            }
            if (me.getChangedProperties().contains("maximum")) {
                System.out.println("new max: " + 
                    ((BoundedRangeModel) e.getSource()).getMaximum()); 
            }
        } else {
            // do something else or nothing
        }
    }
};
slider.getModel().addChangeListener(l);

Note that you have to register the listener with the model, not with the slider (reason being that the slider creates a new changeEvent of the plain type)

OTHER TIPS

You could check who triggered the change in the listener. It's still pretty dirty but you won't have to remove the change listener.

It looks like the same problem, in essence, as this question. In which case, I fear the answer is "no"

if you just need your slider to fire events when value is changing you can simplify kleopatra's answer in the following way:

// make sure slider only fires changEvents when value changes
slider.setModel(new DefaultBoundedRangeModel() {
        final ChangeEvent theOne=new ChangeEvent(this);         
        @Override
        public void setRangeProperties(int newValue, int newExtent, int newMin,int newMax, boolean adjusting) 
            changeEvent= (getValue() != newValue ? theOne:null);
            super.setRangeProperties(newValue, newExtent, newMin, newMax, adjusting);
        }
        @Override
        protected void fireStateChanged()
        {
            if(changeEvent==null) return;
            super.fireStateChanged();
        }
    });

Then all you need to do is register a "Standard" ChangeListener on the model and it will only get called when the value is changing not when maximum or minimum changes.

slider.getModel().addChangeListener(new ChangeListener() {
    public void stateChanged(ChangeEvent e) { 
        // do something here when value changes 
    });

});

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top