0

I tried to implement my own JSpinner model to accept an enumeration (including I18N), so I did like that:

searchSpinner.setModel(new AbstractSpinnerModel() {

    int index = 0;
    int minIndex = 0;
    int maxIndex = MY_ENUM.values().length - 1;
    Object selected = MY_ENUM.values()[index];

    @Override
    public Object getValue() {
        return selected;
    }

    @Override
    public void setValue(Object value) {
        selected = value;
        fireStateChanged();
    }

    @Override
    public Object getNextValue() {
        if (index < maxIndex) {
            index++;
        }
        fireStateChanged();
        return MY_ENUM.values()[index];
    }

    @Override
    public Object getPreviousValue() {
        if (index > minIndex) {
            index--;
        }
        fireStateChanged();
        return MY_ENUM.values()[index];
    }

    @Override
    public void addChangeListener(ChangeListener l) {

    }

    @Override
    public void removeChangeListener(ChangeListener l) {

    }
});

The problem is that did not work, and even the spinner list looks like disabled. What am I doing wrong?

UPDATE: Based on first answer

mKorbel
  • 109,525
  • 20
  • 134
  • 319
Houssam Badri
  • 2,441
  • 3
  • 29
  • 60

2 Answers2

3

You should extend from AbstractSpinnerModel (note to folks new to his question -- note that his original question had the class implementing the SpinnerModel interface. He later changed his code to reflect my recommendation) and be sure to call the fireStateChanged() method when appropriately. Also you've not taken into account edge cases and beyond edge cases.

e.g.,

import javax.swing.*;
import javax.swing.JSpinner.DefaultEditor;

public class MySpinnerPanel extends JPanel {
   public static void main(String[] args) {
      JSpinner spinner = new JSpinner(new MyEnumSpinnerModel());
      JSpinner.DefaultEditor editor = (DefaultEditor) spinner.getEditor();
      editor.getTextField().setColumns(5);

      JPanel panel = new JPanel();
      panel.add(spinner);
      JOptionPane.showMessageDialog(null, panel);
   }
}

enum MyEnum {
   FE, FI, FO, FUM, FOO, FUBAR, SPAM
}

class MyEnumSpinnerModel extends AbstractSpinnerModel {
   private int index = 0;

   @Override
   public Object getValue() {
      return MyEnum.values()[index];
   }

   @Override
   public void setValue(Object value) {
      if (value instanceof MyEnum) {
         index = ((MyEnum) value).ordinal();
         fireStateChanged();
      } else {
         String text = value.toString() + " is not a valid enum item";
         throw new IllegalArgumentException(text);
      }
   }

   @Override
   public Object getNextValue() {
      if (index >= MyEnum.values().length - 1) {
         return null;
      } else {
         return MyEnum.values()[index + 1];
      }
   }

   @Override
   public Object getPreviousValue() {
      if (index <= 0) {
         return null;
      } else {
         return MyEnum.values()[index - 1 ];
      }
   }
}

Edit

Note that the model itself should not require a listener to notify the view (as per the other answer to this question) as that's what the AbstractSpinnerModel does internally. It's fireStateChange() method is what the model itself should call to trigger this notification, same as most all other similar model structures in Swing such as any TableModel object that you create that derives from the AbstractTableModel. For details, please see the source code for the SpinnerListModel. Your code should emulate this class.

Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
  • @houssem note that the answer that you accepted is a kludge and is not correct. – Hovercraft Full Of Eels May 25 '15 at 12:50
  • @HoussemBdr: note edit to code. Regarding my comment above, the model itself should not require a listener to notify the view as that's what the AbstractSpinnerModel does internally. It's `fireStateChange()` method is what the model itself should call to trigger this notification, same as most all other similar model structures in Swing. For details, please see the source code for the [SpinnerListModel](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8-b132/javax/swing/SpinnerListModel.java). Your code should emulate this class. – Hovercraft Full Of Eels May 25 '15 at 19:41
1

You should use ChangeListener to notify the view of changes in the model.

spinner = new JSpinner(new SpinnerModel() {
    private ChangeListener l;

    @Override
    public void setValue(Object value) {
        ...

        if(l != null) {
            l.stateChanged(new ChangeEvent(this));
        }
    }

    ...

    @Override
    public void addChangeListener(ChangeListener l) {
        this.l = l;
    }

    @Override
    public void removeChangeListener(ChangeListener l) {
        if(this.l == l) {
            this.l = null;
        }
    }
});

Edit: You can use List to register many listeners.

mr mcwolf
  • 2,574
  • 2
  • 14
  • 27
  • Juste change this: if(this.l == l) with if(this.l != l), ur answer works fine – Houssam Badri May 25 '15 at 12:31
  • This is not a mistake. To properly remove a listener, it must be submitted the same listener. In this case there is not much matter, but if it is very important to indicate what was removed. – mr mcwolf May 25 '15 at 12:38
  • This answer is incorrect as the model should never require a listener to notify it of its own changes. The correct solution is to fire the notification method. – Hovercraft Full Of Eels May 25 '15 at 13:30
  • This method **is part of the model**. I'm talking about direct implementation of the base interface, and you use for its implementation which has a method by which notifies listeners. – mr mcwolf May 25 '15 at 13:50
  • Please see edit to my answer as well as the source code to the [SpinnerListModel](http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8-b132/javax/swing/SpinnerListModel.java) to see why this answer is incorrect. Also look at the [JTable tutorial](http://docs.oracle.com/javase/tutorial/uiswing/components/table.html) to see a description of how to extend abstract Swing models, in particular the "Firing Data Change Events" section. – Hovercraft Full Of Eels May 25 '15 at 19:45
  • what is the difference between an abstract class and interface? of course if abstratsten expand the class will use the logic in it. but if you implement complicated Interfest must realize all the functionality. and of course that lessons for beginners will enjoy abstract classes because they realizhirat logic which can be complicated for novice (easier with them) – mr mcwolf May 26 '15 at 05:36