0

I am designing a program where I have a set of JSpinner objects and I need to make sure that the total of all objects never exceeds a specified value. Is there a built-in way of doing this, or do I need to roll my own?

By way of example, here's the current interface:

SM attribute interface

I want to make sure that changing a JSpinner updates the remaining at the top, but doesn't allow it to go below 0.

From what I can see, ChangeListener only notifies after the change, with no way of stopping it.

  • Did you go to the JSpinner tutorial? It tells you *exactly* how to do this. If you Google: `JSpinner tutorial` it's the very first hit. – Hovercraft Full Of Eels Oct 24 '16 at 23:42
  • Also the JSpinner API and the API for its number model will tell you all you need to know as well. I'm frankly a bit surprised that you appear to have asked this question before checking out these standard resources. What gives? – Hovercraft Full Of Eels Oct 24 '16 at 23:42
  • 1
    This question was originally closed as a duplicate of: http://stackoverflow.com/questions/15880844/how-to-limit-jspinner. I don't think it is a duplicate (so I reopened the question) since that only shows how to limit an individual spinner. This question is about limiting a total value of all spinners. – camickr Oct 25 '16 at 01:15
  • @camickr is correct. This is about limiting the TOTAL of all spinners, not the value of one. – Ben Jaguar Marshall Oct 25 '16 at 02:23
  • @HovercraftFullOfEels, I searched all over the net, but couldn't find and answer. The first link when searching is https://docs.oracle.com/javase/tutorial/uiswing/components/spinner.html. This only shows how to limit a SINGLE JSpinner, not the COMBINED TOTAL of all of them. – Ben Jaguar Marshall Oct 25 '16 at 02:24
  • @BenJaguarMarshall, the tutorial shows how to link to spinners. In the `SpinnerDemo2` example if you increase the months it will wrap increase the year when you go from December to January. So the point of the link is you need to create your own spinner model that is able to look at the total value of all spinner models. So yes you need to write your own. – camickr Oct 25 '16 at 02:29
  • @camickr Just looked through the `SpinnerDemo2` example and I see what you mean. The `getNextValue` method appears to be the place to look. – Ben Jaguar Marshall Oct 25 '16 at 02:43

1 Answers1

1

I'm sure there is a better way to do this but here is something really basic to give you some ideas.

You can only increase the spinners to a total value of 10:

import java.awt.*;
import java.util.List;
import java.util.ArrayList;
import javax.swing.*;

public class GroupSpinner
{
    private int groupMaximum;

    private List<GroupSpinnerNumberModel> models = new ArrayList<GroupSpinnerNumberModel>();

    public GroupSpinner(int maximum)
    {
        this.groupMaximum = maximum;
    }

    public SpinnerNumberModel createGroupModel(int value, int minimum, int maximum, int step)
    {
        GroupSpinnerNumberModel model = new GroupSpinnerNumberModel(value, minimum, maximum, step, this);
        models.add( model );

        return model;
    }

    public Object getNextValue(int currentValue, int step)
    {
        int maximum = getGroupValue() + step;

        if (maximum > groupMaximum)
        {
            return currentValue;
        }
        else
        {
            groupValueUpdated(maximum);
            int nextValue = currentValue + step;
            return nextValue;
        }
    }

    public int getGroupValue()
    {
        int maximum = 0;

        for (GroupSpinnerNumberModel model: models)
        {
            maximum += model.getNumber().intValue();
        }

        return maximum;
    }

    private void groupValueUpdated(int value)
    {
        System.out.println(value);
    }

    public class GroupSpinnerNumberModel extends SpinnerNumberModel
    {
        private GroupSpinner model;

        public GroupSpinnerNumberModel(int value, int minimum, int maximum, int step, GroupSpinner model)
        {
            super(value, minimum, maximum, step);

            this.model = model;
        }

        public Object getNextValue()
        {
            int currentValue = super.getNumber().intValue();
            int step = super.getStepSize().intValue();

            return model.getNextValue(currentValue, step);
        }
    }

    private static void createAndShowGUI()
    {
        JPanel panel = new JPanel( new GridLayout(0, 1) );

        GroupSpinner group = new GroupSpinner(10);

        JSpinner number1 = new JSpinner( group.createGroupModel(0, 0, 10, 1) );
        panel.add(number1);
        panel.add( new JSpinner( group.createGroupModel(0, 0, 10, 1) ) );
        panel.add( new JSpinner( group.createGroupModel(0, 0, 10, 1) ) );
        panel.add( new JSpinner( group.createGroupModel(0, 0, 10, 1) ) );

        JFrame frame = new JFrame("SSCCE");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(panel);
        frame.pack();
        frame.setLocationByPlatform( true );
        frame.setVisible( true );
    }

    public static void main(String[] args)
    {
        EventQueue.invokeLater( () -> createAndShowGUI() );
/*
        EventQueue.invokeLater(new Runnable()
        {
            public void run()
            {
                createAndShowGUI();
            }
        });
*/
    }

}

Basically the code is overriding the getNextValue() method of the SpinnerModel to determine if you are at the maximum or not. The value is only updated when you are below the group maximum.

camickr
  • 321,443
  • 19
  • 166
  • 288
  • Looks like a good basic implementation. I had started investigating `VetoableChangeListener`, but this looks simpler and more elegant. Just one question: what is the difference between returning **`null`** and the value of `super.getValue()`? – Ben Jaguar Marshall Oct 25 '16 at 02:46
  • @BenJaguarMarshall, don't know if there is a difference. I updated the approach to add the `groupValueUpdated(...)` method. The idea here is that whenever a value changes you can invoke some code. Maybe you pass in a label and you can display a current total. Of course you would also need to implement the `getPreviousValue()` logic as well. – camickr Oct 25 '16 at 03:11