Ich hab nun doch noch was gebastelt. Ausgehend von der Lösung von @Timothy_Truckle habe ich seine Lösung erweitert, unterteilt, parametrisierbar gemacht und die Möglichkeit hinzugefügt, über die Auswahl einer der Buttons informiert zu werden.
Insbesondere die Darstellung der Dreiecke habe ich - optisch gleichbleibend - kräftig überarbeitet, bis ich genau verstand, was da passiert. :o)
[SPOILER]Startklasse:
import java.awt.Color;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.SwingUtilities;
/**
* Diese Klasse testet ArrowSelector-Elemente.
*
* Siehe http://forum.byte-welt.net/threads/12503-Eigene-Checkbox-erstellen
* den Post von Timothy_Truckle.
*
* @version 1.01 2014-07-24
* @author Crian
*/
public class ArrowSelectorTest {
/** Startpunkt des Programms, Parameter werden ignoriert. */
public static void main(String[] args) {
new ArrowSelectorTest();
}
/** Konstruktor. */
public ArrowSelectorTest() {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
createAndShowGUI();
}
});
}
/** Erstellt die grafische Oberfläche und zeigt sie an. */
private void createAndShowGUI() {
JFrame frame = new JFrame("SliderTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridLayout(0, 1));
add(frame, "Standard 9 auf beiden Seiten:", new ArrowSelector());
add(frame, "9 auf beiden Seiten andere Farben:",
new ArrowSelector(Color.RED, Color.ORANGE));
add(frame, "3 auf beiden Seiten:", new ArrowSelector(-3, 3));
add(frame, "3 auf beiden Seiten mit anderen Farben:",
new ArrowSelector(Color.RED, Color.GREEN, -3, 3));
add(frame, "5 rechts mit Rechteck:",
new ArrowSelector(Color.RED, Color.BLACK, 0, 5));
add(frame, "5 rechts ohne Rechteck:",
new ArrowSelector(Color.ORANGE, Color.RED, 1, 5));
add(frame, "4 links ohne Rechteck:", new ArrowSelector(-9, -6));
frame.pack();
frame.setVisible(true);
}
/**
* Fügt einen ArrowSelektor zum Frame hinzu.
*
* @param frame
* Frame, in den eingefügt wird.
* @param message
* Titel des ArrowSelectors.
* @param arrowSelector
* Anzuzeigender ArrowSelektor.
*/
private void add(final JFrame frame, final String message,
final ArrowSelector arrowSelector) {
frame.add(new JLabel(message));
frame.add(arrowSelector);
arrowSelector.addArrowSelectorListener(
new ArrowSelectorListener() {
@Override
public void reactOnButtonSelection(int place) {
System.out.println("Änderung im ArrowSelektor '"
+ message + "':
Ausgewählt ist nun Platz "
+ place + "!");
}
});
}
}
Der eigentliche ArrowSelector:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.ButtonGroup;
import javax.swing.JPanel;
/**
* Diese Klasse stellt eine Komponente dar, auf der man eine Pfeiltaste
* auswählen kann. In der Mitte steht ein Rechteck zur Verfügung.
*
* Siehe http://forum.byte-welt.net/threads/12503-Eigene-Checkbox-erstellen
* den Post von Timothy_Truckle.
*
* @version 1.01 2014-07-24
* @author Crian
*/
public class ArrowSelector extends JPanel {
private static final long serialVersionUID = -5111966252366706268L;
/** Defaultfarbe für selektierte Pfeile. */
static final Color SELECTED_COLOR = Color.YELLOW;
/** Defaultfarbe für nicht selektierte Pfeile. */
static final Color NOT_SELECTED_COLOR = Color.BLUE;
/** Maximale Anzahl an Pfeilen pro Seite. */
static final int MAXIMAL_NUMBER_OF_ARROWS_PER_SIDE = 9;
/** Farbe für selektierte Pfeile. */
private final Color selectedColor;
/** Farbe für nicht selektierte Pfeile. */
private final Color notSelectedColor;
/**
* Platz des Pfeils der am weitesten links ist (negative Zahl, wenn Pfeile
* links da sein sollen).
*/
private final int leftestArrowPlace;
/**
* Platz des Pfeils der am weitesten rechts ist (positive Zahl, wenn Pfeile
* rechts da sein sollen).
*/
private final int rightetsArrowPlace;
/** Verwaltung der Listener. */
private final List<ArrowSelectorListener> listeners;
/** Konstruktor. */
public ArrowSelector() {
this(SELECTED_COLOR, NOT_SELECTED_COLOR,
-MAXIMAL_NUMBER_OF_ARROWS_PER_SIDE,
MAXIMAL_NUMBER_OF_ARROWS_PER_SIDE);
}
/**
* Konstruktor.
*
* @param selectedColor
* Farbe für selektierte Pfeile.
* @param notSelectedColor
* Farbe für nicht selektierte Pfeile.
*/
public ArrowSelector(final Color selectedColor,
final Color notSelectedColor) {
this(selectedColor, notSelectedColor,
-MAXIMAL_NUMBER_OF_ARROWS_PER_SIDE,
MAXIMAL_NUMBER_OF_ARROWS_PER_SIDE);
}
/**
* Konstruktor.
*
* @param leftestArrowPlace
* Platz des Pfeils der am weitesten links ist (negative Zahl,
* wenn Pfeile links da sein sollen).
* @param rightetsArrowPlace
* Platz des Pfeils der am weitesten rechts ist (positive Zahl,
* wenn Pfeile rechts da sein sollen).
*/
public ArrowSelector(final int leftestArrowPlace,
final int rightetsArrowPlace) {
this(SELECTED_COLOR, NOT_SELECTED_COLOR,
leftestArrowPlace, rightetsArrowPlace);
}
/**
* Konstruktor.
*
* @param selectedColor
* Farbe für selektierte Pfeile.
* @param notSelectedColor
* Farbe für nicht selektierte Pfeile.
* @param leftestArrowPlace
* Platz des Pfeils der am weitesten links ist (negative Zahl,
* wenn Pfeile links da sein sollen).
* @param rightetsArrowPlace
* Platz des Pfeils der am weitesten rechts ist (positive Zahl,
* wenn Pfeile rechts da sein sollen).
*/
public ArrowSelector(final Color selectedColor,
final Color notSelectedColor, final int leftestArrowPlace,
final int rightetsArrowPlace) {
super(new GridLayout(1, 0));
this.selectedColor = selectedColor;
this.notSelectedColor = notSelectedColor;
this.leftestArrowPlace = leftestArrowPlace;
this.rightetsArrowPlace = rightetsArrowPlace;
listeners = new ArrayList<>();
if (-leftestArrowPlace > MAXIMAL_NUMBER_OF_ARROWS_PER_SIDE) {
throw new IllegalArgumentException("Zu viele Pfeile links "
+ "gewählt. Maximal sind "
+ MAXIMAL_NUMBER_OF_ARROWS_PER_SIDE
+ " erlaubt, gewählt wurden " + (-leftestArrowPlace) + ".");
}
if (rightetsArrowPlace > MAXIMAL_NUMBER_OF_ARROWS_PER_SIDE) {
throw new IllegalArgumentException("Zu viele Pfeile rechts "
+ "gewählt. "
+ "Maximal sind " + MAXIMAL_NUMBER_OF_ARROWS_PER_SIDE
+ " erlaubt, gewählt wurden " + rightetsArrowPlace + ".");
}
createArrows();
setPreferredSize(new Dimension(4 * 190, 4 * 10));
}
/** Erstellt die anzuzeigenden Pfeile. */
private void createArrows() {
ButtonGroup buttonGroup = new ButtonGroup();
for (int place = leftestArrowPlace;
place <= rightetsArrowPlace; place++) {
addButton(buttonGroup, place);
}
}
/**
* Erzeugt einen Pfeil.
*
* @param buttonGroup
* Gruppe, zu der der Pfeil-Button hinzugefügt wird.
* @param place
* Platz des Pfeiles.
*/
private void addButton(final ButtonGroup buttonGroup, final int place) {
ArrowButton button = new ArrowButton(this, place,
selectedColor, notSelectedColor);
button.setName(String.format("%s", place));
button.setRolloverEnabled(false);
button.setPressedIcon(null);
button.setSelectedIcon(null);
button.setPressedIcon(null);
add(button);
buttonGroup.add(button);
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
for (ArrowSelectorListener listener : listeners) {
listener.reactOnButtonSelection(place);
}
}
});
}
/**
* Fügt einen Listener zu der Liste der Listener hinzu.
*
* @param listener
* Hinzuzufügender Listener.
*/
public void addArrowSelectorListener(
final ArrowSelectorListener listener) {
listeners.add(listener);
}
}
Der einzelne ArrowButton:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Rectangle;
import java.awt.Shape;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import static such.dir.eins.aus.ArrowSelector.MAXIMAL_NUMBER_OF_ARROWS_PER_SIDE;
/**
* Diese Klasse stellt einen JRadioButton dar, der sich als Pfeildarstellung
* präsentiert.
*
* Siehe http://forum.byte-welt.net/threads/12503-Eigene-Checkbox-erstellen
* den Post von Timothy_Truckle.
*
* @version 1.01 2014-07-24
* @author Crian
*/
public class ArrowButton extends JRadioButton {
private static final long serialVersionUID = -8818232289745150208L;
/** Unterteilung der Höhe für das Rechteck und die Dreiecke. */
private static final int HEIGHT_DIVISOR = 4;
/** Unterteilung der Breite für das Rechteck. */
private static final int WIDTH_DIVISOR = 4;
/** Abstand der Pfeildarstellung vom Rand in X-Richtung. */
private static final int ARROW_DISTANCE = 2;
/** Das Panel, in dem dieser ArrowButton platziert wird. */
private final JPanel surroundingPanel;
/**
* Platz oder Position des ArrowButtons. 0 bezeichnet die Mitte, negative
* Werte Plätze links der Mitte, positive Werte rechts der Mitte.
*/
private final int place;
/** Farbe für selektierte Pfeile. */
private final Color selectedColor;
/** Farbe für nicht selektierte Pfeile. */
private final Color notSelectedColor;
/**
* Konstruktor.
*
* @param surroundingPanel
* Das Panel, in dem dieser ArrowButton platziert wird.
* @param place
* Platz oder Position des ArrowButtons. 0 bezeichnet die Mitte,
* negative Werte Plätze links der Mitte, positive Werte rechts
* der Mitte.
* @param selectedColor
* Farbe für ausgewählte Pfeile.
* @param notSelectedColor
* Farbe für nicht ausgewählte Pfeile.
*/
public ArrowButton(final JPanel surroundingPanel, final int place,
final Color selectedColor, final Color notSelectedColor) {
super();
this.surroundingPanel = surroundingPanel;
this.place = place;
this.selectedColor = selectedColor;
this.notSelectedColor = notSelectedColor;
}
/**
* Stellt den ArrowButton dar, falls das Objekt sichtbar ist.
*
* @param graphics
* Grafikobjekt.
*/
@Override
public void paint(final Graphics graphics) {
if (isShowing()) {
paintTheArrow((Graphics2D) graphics);
}
}
/**
* Stellt den ArrowButton dar.
*
* @param graphics2d
* Grafikobjekt.
*/
private void paintTheArrow(final Graphics2D graphics2d) {
Dimension size = getSize();
graphics2d.setBackground(surroundingPanel.getBackground());
graphics2d.clearRect(0, 0, size.width, size.height);
Shape shape = null;
if (0 == place) {
shape = createRectangle(size);
}
else {
shape = createArrow(size);
}
graphics2d.setColor(isSelected()
? selectedColor
: notSelectedColor);
graphics2d.fill(shape);
}
/**
* Erzeugt das Rechteck. Die Größe wird übergeben und nicht nochmal
* abgefragt, damit während einer Größenveränderung nicht bei mehrfachen
* Abfragen der Werte unterschiedliche Größen verwendet werden.
*
* @param size
* Größe des Buttons.
* @return Erzeugtes Rechteck.
*/
private Rectangle createRectangle(final Dimension size) {
int x = size.width / WIDTH_DIVISOR;
int y = size.height / HEIGHT_DIVISOR;
int width = size.width / 2;
int height = size.height / 2;
return new Rectangle(x, y, width, height);
}
/**
* Erzeugt einen Pfeil. Die Größe wird übergeben und nicht nochmal
* abgefragt, damit während einer Größenveränderung nicht bei mehrfachen
* Abfragen der Werte unterschiedliche Größen verwendet werden.
*
* Links zeigt die Spitze nach links, rechts zeigt sie nach rechts.
*
* @param size
* Größe des Buttons.
* @return Erzeugter Pfeil.
*/
private Polygon createArrow(final Dimension size) {
/* Minimale Höhe, entspricht der des Rechtecks (1/4): */
int minHeight = size.height / HEIGHT_DIVISOR;
/* Maximale Höhe (3/4): */
int maxHeight = minHeight * (HEIGHT_DIVISOR - 1);
/* Höhendifferenz oben (oder unten, also 1/4): */
int totalHeightDifference = (maxHeight - minHeight) / 2;
/* Höhendifferenz für einen Schritt: */
int heightDifferenceStep = totalHeightDifference /
MAXIMAL_NUMBER_OF_ARROWS_PER_SIDE;
/* Höhendifferenz für den Platz: */
int heightDifference = (MAXIMAL_NUMBER_OF_ARROWS_PER_SIDE -
Math.abs(place)) * heightDifferenceStep;
/* Benötigte x-Koordinaten: */
int leftX = ARROW_DISTANCE;
int rightX = size.width - ARROW_DISTANCE;
/* Benötigte y-Koordinaten: */
int upperY = heightDifference;
int middleY = size.height / 2;
int lowerY = size.height - heightDifference;
/* x-Koordinatentausch für die linken Dreiecke: */
if (place < 0) {
int temp = rightX;
rightX = leftX;
leftX = temp;
}
/* Dreieck erzeugen: */
Polygon polygon = new Polygon();
polygon.addPoint(leftX, upperY);
polygon.addPoint(rightX, middleY);
polygon.addPoint(leftX, lowerY);
return polygon;
}
}
und das Interface ArrowSelectorListener:
/**
* Dieses Interface dient zum Reagieren auf eine Auswahl im ArrowSelektor.
*
* @version 1.01 2014-07-24
* @author Crian
*/
public interface ArrowSelectorListener {
void reactOnButtonSelection(int place);
}```[/SPOILER]