You are close.
You need indeed to fire an event when adding and removing elements from the list, but this is the responsibility of the model, not the spinner. So you have to implement your own model.
The other problem is when the list gets emptied, then there is nothing for the spinner to display and that's probably why you are getting the exception.
So you have to decide what to do when the spinner gets emptied.
Let's say for example you want the spinner to get disabled when it is empty and to get back to enabled when it is not empty.
As I told you earlier, you have to implement your SpinnerListModel
so as to fire manually the events for adding and removing. But now you also want the spinner to get disabled and enabled, according to the model. So you can implement the model to get the reference of the spinner...
Follows sample code:
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.SpinnerListModel;
public class ImageDealer2 {
public static class RemoveSpinnerListModel extends SpinnerListModel {
private static final String VALUE_OF_EMPTY_LIST = ""; //This value indicates the spinner is empty.
protected JSpinner spin; //The spinner which this model referes to.
private boolean empty; //indicates if the list spinner is empty.
//The spinner is empty if it contains only the VALUE_OF_EMPTY_LIST value.
public RemoveSpinnerListModel() {
super(new ArrayList(Arrays.asList(VALUE_OF_EMPTY_LIST))); //We initialize to an "empty" read-write list.
empty = true; //The spinner does not contain any valid value.
}
public void setSpinner(final JSpinner spin) {
this.spin = Objects.requireNonNull(spin); //Just a null check plus assignment.
//We ensure the list has at least one element in it (the VALUE_OF_EMPTY_LIST):
if (getList().isEmpty())
((List)getList()).add(VALUE_OF_EMPTY_LIST);
//The spinner is empty if and only if the list contains only the VALUE_OF_EMPTY_LIST.
empty = getList().size() == 1 && getList().get(0).equals(VALUE_OF_EMPTY_LIST);
//We enable/disable the spinner accordingly:
spin.setEnabled(!empty);
if (empty) //If the spinner is empty of valid values:
spin.setValue(VALUE_OF_EMPTY_LIST); //then we add the VALUE_OF_EMPTY_LIST.
}
public void add(final Object value) {
if (empty) //If the spinner contains only the VALUE_OF_EMPTY_LIST, then:
getList().clear(); //Remove the VALUE_OF_EMPTY_LIST string.
((List)getList()).add(value); //Add the requested value.
if (spin != null) {
spin.setEnabled(true); //Enable the spinner for sure, because now it has at least one valid value.
spin.setValue(value);
}
empty = false; //The spinner is surely not empty.
fireStateChanged(); //Important step: updates the spinner via firing an event in the model.
}
public void remove(final Object value) {
if (!getList().isEmpty()) { //If there is something to remove.
getList().remove(value); //Remove the requested value.
if (getList().isEmpty()) //If now the list is empty, then we mark the spinner as empty:
markTheSpinnerAsEmpty();
else //Else the list still contains valid elements, so we mark the spinner as not-empty:
empty = false;
}
else //Else, the list is empty, so nothing to remove and the spinner must be marked as empty:
markTheSpinnerAsEmpty();
if (spin != null)
spin.setEnabled(!empty);
fireStateChanged(); //Important step: updates the spinner via firing an event in the model.
}
public void clear() {
getList().clear(); //Remove everything for sure.
markTheSpinnerAsEmpty();
}
private void markTheSpinnerAsEmpty() {
((List)getList()).add(VALUE_OF_EMPTY_LIST);
if (spin != null) {
spin.setValue(VALUE_OF_EMPTY_LIST); //Let's show the user that nothing is here.
spin.setEnabled(false);
}
empty = true;
}
}
protected JFrame selectCoverFrame;
protected JSpinner spinnerCovers;
protected JButton deleteCoverButton;
protected RemoveSpinnerListModel spinnerCoversM;
public ImageDealer2() {
selectFrameInit();
}
private void selectFrameInit() {
selectCoverFrame = new JFrame("Select");
selectCoverFrame.setSize(new Dimension(500, 100));
selectCoverFrame.getContentPane().setLayout(new BoxLayout(selectCoverFrame.getContentPane(), BoxLayout.Y_AXIS));
spinnerCoversM = new RemoveSpinnerListModel();
spinnerCoversM.add("a");
spinnerCoversM.add("b");
spinnerCoversM.add("c");
spinnerCoversM.add("d");
spinnerCovers = new JSpinner(spinnerCoversM);
spinnerCoversM.setSpinner(spinnerCovers); //DO NOT forget this step!
deleteCoverButton = new JButton("Delete current element");
DeleteCurrentCoverHandler deleteCurrentCoverHandler = new DeleteCurrentCoverHandler();
deleteCoverButton.addActionListener(deleteCurrentCoverHandler);
final JButton addButton = new JButton("Add an element");
AddANewCoverHandler addANewCoverHandler = new AddANewCoverHandler();
addButton.addActionListener(addANewCoverHandler);
final JPanel buttonPanel = new JPanel(); //FlowLayout.
buttonPanel.add(deleteCoverButton);
buttonPanel.add(addButton);
selectCoverFrame.getContentPane().add(spinnerCovers);
selectCoverFrame.getContentPane().add(buttonPanel);
selectCoverFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //With this call, when you close the frame, then the application gets terminated.
selectCoverFrame.setLocationRelativeTo(null); //With this call, the frame gets to the center of the screen.
selectCoverFrame.setVisible(true);
}
private class DeleteCurrentCoverHandler implements ActionListener {
@Override
public void actionPerformed(final ActionEvent e) {
spinnerCoversM.remove(spinnerCovers.getValue());
}
}
private class AddANewCoverHandler implements ActionListener {
@Override
public void actionPerformed(final ActionEvent e) {
spinnerCoversM.add(JOptionPane.showInputDialog("Enter the new element string:"));
}
}
public static void main(final String[] args) {
new ImageDealer2();
}
}
If you want to add or remove any objects from the spinner, then you have to do it via this model.
For any other "read-only" action though (such as isEmpty()
and size()
) you can call the getList()
of the model.
Maybe this is a bigger than expected solution, but it's reusable.