Search code examples
javaswingjslider

How to update a JSlider's maximum value without dispatching a change event


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?


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)