Ein JSpinner für Bilder

Durch einen Wechsel des Editors kann ein JSpinner zur Auswahl von Bildern oder andere Objekten verwendet werden.

JSpinner mit Bilddateien

Ein JSpinner ist eine einzeilige, editierbare Auswahlkomponente. Er verwendet zur Darstellung seiner Inhalte ein JFormattedTextField. Somit eignen sich hierfür standardmäßig nur textbasierte Werte.
Das Textfeld ist, je nach Charakter des darzustellenden Inhaltes, in einem spezialisierten Editor gekapselt, dessen Typ von JSpinner.DefaultEditor abgeleitet ist. Dieser wiederum stellt ein JPanel dar, auf dem das erwähnte Textfeld positioniert ist.
Durch Austausch des Textfeldes gegen eine andere Darstellungskomponente und einigen weiteren kleineren Anpassungen lässt sich ein JSpinner auch für nicht textbasierte Objekte verwenden.

class ImageEditor extends JPanel {

    public ImageEditor(ImageSpinner spinner) {
        JLabel label = new JLabel();
        label.setIcon((ImageIcon) spinner.getValue());
        spinner.addChangeListener(e -> label.setIcon((ImageIcon) spinner.getValue()));
        String toolTipText = spinner.getToolTipText();
        if (toolTipText != null) {
            label.setToolTipText(toolTipText);
        }
        add(label);
    }
}

Die Klasse des Editors ist, wie beschrieben, von JPanel abgeleitet. Ihm wird zur Aufnahme der Bilder ein JLabel hinzugefügt. Um auf die Aktivität der Schaltflächen zu reagieren, wird der Spinner bei einem ChangeListener angemeldet, in dessen Methode stateChanged() die jeweils aktuelle Bilddatei dem Label übergeben wird.

Die Bilddateien selbst werden in Form von ImageIcon-Objekten geladen. Sie werden durch die Klasse SpinnerListModel verwaltet, bei dem lediglich die Konstruktoren und Accessormethoden des Models angepasst werden müssen. Zur Steuerung der Dateireihenfolge des Spinners dient ein numerischer Index, der in getNextValue() und getPreviousValue() gleich rotierend inkremetiert oder dekrementiert wird.

class ImageListModel extends SpinnerListModel {

    private List<ImageIcon> list;
    private int index;

    public ImageListModel() {
        this(new ImageIcon[] { null });
    }

    public ImageListModel(ImageIcon[] images) {
        this(Arrays.asList(images));
    }

    public ImageListModel(List<ImageIcon> images) {
        super(images);
        this.list = images;
        this.index = 0;
    }

    @Override
    public Object getValue() {
        return (ImageIcon) list.get(index);
    }

    @Override
    public Object getNextValue() {
        index = (index >= (list.size() - 1)) ? 0 : (index + 1);
        return (ImageIcon) list.get(index);
    }

    @Override
    public Object getPreviousValue() {
        index = (index <= 0) ? list.size() - 1 : (index - 1);
        return (ImageIcon) list.get(index);
    }
}

Es verbleibt die Anpassung des Spinners selbst. Hierzu müssen in der JSpinner-Klasse, neben der Anpassung der Konstruktoren, ebenfalls nur die Methoden setEditor() und setModel() überschrieben werden, um die Sicherheitsabfragen auf die neuen Model- und Editor-Klassen anzupassen.

@Override
    public void setModel(SpinnerModel model) {
        super.setModel(model);
        if (!(model instanceof ImageListModel))
            throw new IllegalArgumentException("Model must be of type ImageListModel");
    }

    @Override
    public void setEditor(JComponent editor) {
        super.setEditor(editor);
        if (!(editor instanceof ImageEditor))
            throw new IllegalArgumentException("Editor must be of type ImageEditor");
    }

Vollständiger Quelltext

Nicht vergessen, den Pfad zu den Bilddateien in loadImg() anzupassen!

import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.SpinnerListModel;
import javax.swing.SpinnerModel;
import javax.swing.SwingUtilities;

public class SpinnerMitBildern {

    public SpinnerMitBildern() {
        init();
    }

    public void init() {
        ImageSpinner spinner = new ImageSpinner(new ImageListModel(loadImg()));

        JFrame frame = new JFrame("JSpinner mit Bildern");
        frame.add(spinner);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);
    }

    private ImageIcon[] loadImg() {
        BufferedImage img = null;
        ImageIcon[] images = new ImageIcon[4];
        try {
            for (int i = 0; i < images.length; i++) {
                img = ImageIO.read(getClass().getResource("../../img/js" + i + ".png"));
                images[i] = new ImageIcon(img);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return images;
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> new SpinnerMitBildern());
    }
}

class ImageListModel extends SpinnerListModel {

    private List<ImageIcon> list;
    private int index;

    public ImageListModel() {
        this(new ImageIcon[] { null });
    }

    public ImageListModel(ImageIcon[] images) {
        this(Arrays.asList(images));
    }

    public ImageListModel(List<ImageIcon> images) {
        super(images);
        this.list = images;
        this.index = 0;
    }

    @Override
    public Object getValue() {
        return (ImageIcon) list.get(index);
    }

    @Override
    public Object getNextValue() {
        index = (index >= (list.size() - 1)) ? 0 : (index + 1);
        return (ImageIcon) list.get(index);
    }

    @Override
    public Object getPreviousValue() {
        index = (index <= 0) ? list.size() - 1 : (index - 1);
        return (ImageIcon) list.get(index);
    }
}

class ImageEditor extends JPanel {

    public ImageEditor(ImageSpinner spinner) {
        JLabel label = new JLabel();
        label.setIcon((ImageIcon) spinner.getValue());
        spinner.addChangeListener(e -> label.setIcon((ImageIcon) spinner.getValue()));
        String toolTipText = spinner.getToolTipText();
        if (toolTipText != null) {
            label.setToolTipText(toolTipText);
        }
        add(label);
    }
}

class ImageSpinner extends JSpinner {

    public ImageSpinner() {
        this(new ImageListModel());
    }

    public ImageSpinner(ImageListModel model) {
        super(model);
        setEditor(new ImageEditor(this));
    }

    @Override
    public void setModel(SpinnerModel model) {
        super.setModel(model);
        if (!(model instanceof ImageListModel))
            throw new IllegalArgumentException("Model must be of type ImageListModel");
    }

    @Override
    public void setEditor(JComponent editor) {
        super.setEditor(editor);
        if (!(editor instanceof ImageEditor))
            throw new IllegalArgumentException("Editor must be of type ImageEditor");
    }
}