Put together a RadioMenu
class that can use a Enum to generate a Single-Selection Radio Button Menu. My main question is about whether there's a way to remove the need to pass in the Class of the Enum to the generateButtonsFromEnum()
method. Otherwise, would appreciate any general pointers on ways to improve this system since I'm still pretty new when it comes to AWT/Swing features.
RadioMenu
package tools;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.HashMap;
import javax.swing.JMenu;
import javax.swing.JRadioButtonMenuItem;
public class RadioMenu<E extends Enum<E>> extends JMenu {
private static final long serialVersionUID = 1L;
private E currentState;
private JRadioButtonMenuItem selectedRadioButton;
private HashMap<E, JRadioButtonMenuItem> stateMap;
public RadioMenu() {
stateMap = new HashMap<E, JRadioButtonMenuItem>();
}
public RadioMenu(String name) {
super(name);
stateMap = new HashMap<E, JRadioButtonMenuItem>();
}
public void addRadioButton(E enumValue, JRadioButtonMenuItem radioButton) {
//Set default to first added button
if(stateMap.isEmpty()) {
currentState = enumValue;
radioButton.setSelected(true);
selectedRadioButton = radioButton;
}
add(radioButton);
stateMap.put(enumValue, radioButton);
radioButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setState(enumValue);
}
});
}
public void generateButtonsFromEnum(Class<E> enumType) {
for(E enumValue : enumType.getEnumConstants()) {
addRadioButton(enumValue, new JRadioButtonMenuItem(enumValue.toString()));
}
}
public E getState() {
return currentState;
}
public void setState(E newState) {
currentState = newState;
selectedRadioButton.setSelected(false);
selectedRadioButton = stateMap.get(newState);
selectedRadioButton.setSelected(true);
}
}
RadioMenuTest
package main;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.SwingUtilities;
import tools.RadioMenu;
public class RadioMenuTest implements Runnable {
public enum RadioOptions {
Forward, Backward, Left, Right
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new RadioMenuTest());
}
@Override
public void run() {
JFrame frame = new JFrame("RadioMenu Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setJMenuBar(createMenuBar());
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JMenuBar createMenuBar() {
JMenuBar menuBar = new JMenuBar();
JMenu fileMenu = new JMenu("File");
menuBar.add(fileMenu);
RadioMenu<RadioOptions> optionsMenu = new RadioMenu<RadioOptions>("Options");
optionsMenu.generateButtonsFromEnum(RadioOptions.class);
fileMenu.add(optionsMenu);
return menuBar;
}
}
1 Answer 1
dependency injection
with the feedback from your comments i would provide those <T extends Enum<T>>
that you want to use already in your constructor. That would remove the not-so-handy method generateButtonsFromEnum
.
public enum RadioOptions {Forward, Backward, Left, Right}
RadioMenu<RadioOptions> optionsMenu = new RadioMenu<>(RadioOptions.values());
optionsMenu.setText("Options")
doing so it would also let you create a reduced menu if you don't want to use all enum values:
public enum RadioOptions {Forward, Backward, Left, Right}
RadioOptions[] leftRight = {RadioOptions.Left, RadioOptions.Right}
RadioMenu<RadioOptions> optionsMenu = new RadioMenu<>(leftRight);
optionsMenu.setText("Options")
Such an constructor you would provide an instance of T (as suggested in the comments).
Note
have a look at EnumMap
a specialized map for storing/accessings Enums.
-
1\$\begingroup\$ Hit a snag with
EnumMap
where it requires the Class in the constructor (but I still like it, thanks for the tip). As a result, ended up with a compromise based on this advise where I pass the Class to the constructor and keep agenerateButtons()
method with agenerateButtons(E[] enumValues)
overload variant for generating using a array to keep the flexibility of generating using a subset. Opted to keep agenerateButtons()
method because I prefer that being explicitly executed instead of assumed behavior. \$\endgroup\$Tim Hunter– Tim Hunter2021年06月30日 13:25:21 +00:00Commented Jun 30, 2021 at 13:25 -
1\$\begingroup\$ you made some good points =) a Review is very valuable to those who want to improve their skills and very rarely there is a plain right/wrong answer - thanks for sharing your thoughts! \$\endgroup\$Martin Frank– Martin Frank2021年06月30日 14:05:52 +00:00Commented Jun 30, 2021 at 14:05
RadioOptions[] values = RadioOptions.values();
? doing so you could directly add all possible values (i don't know your assets). \$\endgroup\$main
method:optionsMenu.generateButtonsFromEnum(RadioOptions.values());
\$\endgroup\$class T
of thetype T
at compile time. hence you have to provide either the class T (as you did) or you provide one (or more) instance(s) of T as suggested byenum.values()
\$\endgroup\$