Включение строки/реализация действий кнопки
Вопрос
Полный отказ от ответственности:Я студент CS, и этот вопрос связан с недавно назначенной Java-программой для объектно-ориентированного программирования.Хотя мы уже работали над консолью, мы впервые работаем с графическим интерфейсом и Swing или Awt.Нам дали код, который создавал окно с текстом и кнопку, которая меняла цвет текста.Затем нас попросили изменить программу, чтобы вместо этого создавать переключатели для цветов — это также было предназначено для того, чтобы дать нам возможность попрактиковаться в исследовании API.Я уже сдал задание и получил разрешение от преподавателя опубликовать здесь свой код.
Как лучше всего реализовать действия кнопок в Java?Немного повозившись, я создал такие кнопки:
class HelloComponent3 extends JComponent
implements MouseMotionListener, ActionListener
{
int messageX = 75, messageY= 175;
String theMessage;
String redString = "red", blueString = "blue", greenString = "green";
String magentaString = "magenta", blackString = "black", resetString = "reset";
JButton resetButton;
JRadioButton redButton, blueButton, greenButton, magentaButton, blackButton;
ButtonGroup colorButtons;
public HelloComponent3(String message) {
theMessage = message;
//intialize the reset button
resetButton = new JButton("Reset");
resetButton.setActionCommand(resetString);
resetButton.addActionListener(this);
//intialize our radio buttons with actions and labels
redButton = new JRadioButton("Red");
redButton.setActionCommand(redString);
...
И добавили прослушиватели действий...
redButton.addActionListener(this);
blueButton.addActionListener(this);
...
Для метода actionPerformed уже была создана заглушка, чтобы дать нам представление о том, как его использовать, но поскольку в шаблоне была только одна кнопка, было неясно, как реализовать несколько кнопок.Я попробовал включить String, но быстро понял, что, поскольку String не является примитивным типом, я не могу использовать его для оператора переключения.Я мог бы импровизировать с цепочкой if-else, но вместо этого я придумал вот что.Кажется, это далеко не элегантно, и должен быть лучший способ.Если есть, то что это?Есть ли способ включить строку?Или выбрать действие более масштабируемым образом?
public void actionPerformed(ActionEvent e){
if (e.getActionCommand().equals(resetString)) {
messageX = 75; messageY = 175;
setForeground(Color.black);
blackButton.setSelected(true);
repaint();
return;
}
if ( e.getActionCommand().equals(redString) ) {
setForeground(Color.red);
repaint();
return;
}
if ( e.getActionCommand().equals(blueString) ) {
setForeground(Color.blue);
repaint();
return;
}
if ( e.getActionCommand().equals(greenString) ) {
setForeground(Color.green);
repaint();
return;
}
if ( e.getActionCommand().equals(magentaString) ) {
setForeground(Color.magenta);
repaint();
return;
}
if ( e.getActionCommand().equals(blackString) ) {
setForeground(Color.black);
repaint();
return;
}
}
Решение
Вместо того чтобы писать это
resetButton.addActionListener(this);
Вы также можете написать это:
resetButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent evt) {
resetButtonActionPerformed(evt);
}
});
И вместо того, чтобы писать один большой actionPerformed () для всех действий, вы можете (и тогда должны) написать это:
public void resetButtonActionPerformed(ActionEvent evt) {
messageX = 75; messageY = 175;
setForeground(Color.black);
blackButton.setSelected(true);
repaint();
}
Я не знаю, является ли это наиболее элегантным решением, но, по крайней мере, у вас больше нет такой большой конструкции if.
Другие советы
Два альтернативных подхода:
<Ол>Достаточно приличный подход - объявить перечисление, элементы которого соответствуют вашим строкам и включите valueOf (str) (связанный пример показывает, как сделать это с достаточной степенью безопасности).
Причина, по которой следует избегать анонимных внутренних классов, возможно, заключается в том, что класс не имеет такой конструкции (пока), хотя это может быть лучшим решением.
Как уже предлагалось, вы можете использовать анонимные внутренние классы для реализации интерфейса ActionListener. В качестве альтернативы вам не нужно использовать анонимные внутренние классы, но вместо этого вы можете использовать простой вложенный класс:
resetButton = new JButton(new ResetAction());
redButton = new JButton(new ColorAction("Red", Color.red));
а затем ...
private class ResetAction extends AbstractAction {
public ResetAction() {
super("Reset");
}
public void actionPerformed(ActionEvent e) {
messageX = 75; messageY = 175;
setForeground(Color.black);
blackButton.setSelected(true);
repaint();
}
}
private class ResetAction extends AbstractAction {
private Color color;
public ColorAction(String title, Color color) {
super(title);
this.color = color;
}
public void actionPerformed(ActionEvent e) {
setForeground(color);
repaint();
}
}
Почему этот подход - или любой подход, включающий внутренние классы - лучше, чем реализация ActionListener во внешнем классе, см. в разделе «Шаблоны проектирования»:
" Пользуйся 'композицией объекта' над 'наследованием класса'. " (Банда Четырех, 1995: 20).
Выбор между анонимными внутренними классами и этими именованными внутренними классами во многом зависит от стиля, но я думаю, что эту версию легче понять, и она понятнее при большом количестве действий.
Эрг.Не реализуйте массу несвязанных между собой интерфейсов в одном мегаклассе.Вместо этого используйте анонимные внутренние классы.Они немного многословны, но это то, что вам нужно.Используйте по одному для каждого события, тогда вам не понадобится большая цепочка if-else.Я предлагаю хранить во внутреннем классе достаточно кода для декодирования событий и вызовов методов, которые имеют смысл для целевых объектов.Кроме того, вы можете параметризовать свои внутренние классы.Вероятно, вы обнаружите, что вам не нужно хранить ссылки на реальные виджеты.
В вашем примере вы, похоже, используете JComponent в качестве JPanel.Особой разницы нет, но для сбора блока виджетов используйте JPanel.Кроме того, маловероятно, что потребуется создавать подклассы, так что не делайте этого.
Так, например:
addColorButton("Green" , Color.GREEN );
addColorButton("Red" , Color.RED );
addColorButton("Yellow", Color.YELLOW);
addColorButton("Blue" , Color.BLUE );
...
private void addColorButton(String label, Color color) {
JRadioButton button = new JRadioButton(label);
button.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent event) {
target.setForeground(color);
target.repaint();
}
});
colorGroup.add(button);
panel.add(button);
}