在 java swing 应用程序中分离 GUI 和逻辑代码
Separating GUI and Logic code in a java swing app
我试图将以下代码的逻辑和 UI 设计分离到它们自己的 class 中,但我无法弄清楚如何调用链接到 swing 组件的某些方法我的 GUI class 在 Controller class.
我的问题是,当我在控制器 class 中创建 GUI class 的实例时,G[=25= 中的 JRadioButtons class 变量]只在GUI中的createRBButtonsPanel()方法中初始化。当我尝试调用控制器的 actionPerformed 方法中的 cokeRb.getText() 方法时,这会给我一个 NullPointerException,因为我没有调用导致它们被初始化的事件链。
我试图避免将它们全部设置为静态,因为我确信这样做不是最佳做法。
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class GUI {
private CoinAcceptor coinSlot = new CoinAcceptor();
private VMController controller;
GUI(VMController controller) {
this.controller = controller;
}
private JRadioButton cokeRb, lemonadeRb, tangoRb, waterRb, pepsiRb, spriteRb;
private JButton purchaseBtn , clearBtn, cancelBtn, loyaltyCardBTN;
private JTextField moneyRemainingTf, balanceTf;
private GridBagConstraints c = new GridBagConstraints();
//// Getters /////////////////////////////////
public String getMoneyTFText() { return this.moneyRemainingTf.getText(); }
public String getBalanceTF() { return balanceTf.getText(); }
public String getCokeRbText() { return this.cokeRb.getText(); }
public String getTangoRbText() { return this.tangoRb.getText(); }
public String getLemonadeRbText() { return this.lemonadeRb.getText(); }
public String getWaterRbText() { return this.waterRb.getText(); }
public String getPepsiRbText() { return this.pepsiRb.getText(); }
public String getSpriteRbText() { return this.spriteRb.getText(); }
//// Setters /////////////////////////////////
public void setBalanceTF(String text) { balanceTf.setText(text); }
public void setCokeRbText(String text) { this.cokeRb.setText(text); }
public void setLemonadeRbText(String text) { this.lemonadeRb.setText(text); }
public void setTangoRbText(String text) { this.tangoRb.setText(text); }
public void setWaterRbText(String text) { this.waterRb.setText(text); }
public void setPepsiRbText(String text) { this.pepsiRb.setText(text);}
public void setSpriteRbText(String text) { this.spriteRb.setText(text); }
public void deselectRadioButtons(String button) {
switch (button) {
case "COKE":
this.cokeRb.setSelected(true);
this.lemonadeRb.setSelected(false);
this.tangoRb.setSelected(false);
this.waterRb.setSelected(false);
this.pepsiRb.setSelected(false);
this.spriteRb.setSelected(false);
break;
case "LEMONADE":
this.cokeRb.setSelected(false);
this.lemonadeRb.setSelected(true);
this.tangoRb.setSelected(false);
this.waterRb.setSelected(false);
this.pepsiRb.setSelected(false);
this.spriteRb.setSelected(false);
break;
case "TANGO":
this.lemonadeRb.setSelected(false);
this.cokeRb.setSelected(false);
this.tangoRb.setSelected(true);
this.waterRb.setSelected(false);
this.pepsiRb.setSelected(false);
this.spriteRb.setSelected(false);
break;
case "WATER":
this.lemonadeRb.setSelected(false);
this.tangoRb.setSelected(false);
this.cokeRb.setSelected(false);
this.waterRb.setSelected(true);
this.pepsiRb.setSelected(false);
this.spriteRb.setSelected(false);
break;
case "PEPSI":
this.lemonadeRb.setSelected(false);
this.tangoRb.setSelected(false);
this.waterRb.setSelected(false);
this.cokeRb.setSelected(false);
this.pepsiRb.setSelected(true);
this.spriteRb.setSelected(false);
break;
case "SPRITE":
this.lemonadeRb.setSelected(false);
this.tangoRb.setSelected(false);
this.waterRb.setSelected(false);
this.pepsiRb.setSelected(false);
this.cokeRb.setSelected(false);
this.spriteRb.setSelected(true);
break;
default:
this.lemonadeRb.setSelected(false);
this.tangoRb.setSelected(false);
this.waterRb.setSelected(false);
this.pepsiRb.setSelected(false);
this.cokeRb.setSelected(false);
this.spriteRb.setSelected(false);
break;
}
}
public void clearAllFields() {
deselectRadioButtons("");
balanceTf.setText("");
this.moneyRemainingTf.setText("");
}
public boolean isRBSelected() {
JRadioButton[] radioButtons = new JRadioButton[] { cokeRb, lemonadeRb, tangoRb, waterRb, pepsiRb, spriteRb };
for (JRadioButton button : radioButtons) {
if (button.isSelected())
return true;
}
return false;
}
private JPanel createRBButtonsPanel() {
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(3, 6, 5, 5));
panel.setBorder(BorderFactory.createTitledBorder("Select and item"));
cokeRb = new JRadioButton("Coke : £1.50");
cokeRb.addActionListener(controller);
cokeRb.setActionCommand("COKE");
lemonadeRb = new JRadioButton("Lemonade : £1.20");
lemonadeRb.addActionListener(controller);
lemonadeRb.setActionCommand("LEMONADE");
tangoRb = new JRadioButton("Tango : £1.40");
tangoRb.addActionListener(controller);
tangoRb.setActionCommand("TANGO");
waterRb = new JRadioButton("Water : £1");
waterRb.addActionListener(controller);
waterRb.setActionCommand("WATER");
pepsiRb = new JRadioButton("Pepsi : £1.30");
pepsiRb.addActionListener(controller);
pepsiRb.setActionCommand("PEPSI");
spriteRb = new JRadioButton("Sprite : £1.20");
spriteRb.addActionListener(controller);
spriteRb.setActionCommand("SPRITE");
purchaseBtn = new JButton("PURCHASE");
purchaseBtn.addActionListener(controller);
clearBtn = new JButton("CLEAR");
clearBtn.addActionListener(controller);
cancelBtn = new JButton("CANCEL");
cancelBtn.addActionListener(controller);
panel.add(cokeRb);
panel.add(lemonadeRb);
panel.add(tangoRb);
panel.add(waterRb);
panel.add(pepsiRb);
panel.add(spriteRb);
panel.add(purchaseBtn);
panel.add(clearBtn);
panel.add(cancelBtn);
c.gridx = 0;
c.gridy = 0;
c.insets = new Insets(10, 10, 10, 10);
return panel;
}
private JPanel createInputPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
balanceTf = new JTextField();
// balanceTf.setForeground(Color.gray);
// balanceTf.addFocusListener(new FocusListener() {
//
// @Override
// public void focusGained(FocusEvent e) {
// if (balanceTf.getText().equals("2.50")) {
// balanceTf.setText("");
// balanceTf.setForeground(Color.black);
// }
// }
//
// @Override
// public void focusLost(FocusEvent e) {
// if (balanceTf.getText().isEmpty()) {
// balanceTf.setText("2.50");
// balanceTf.setForeground(Color.gray);
// }
// }
// });
moneyRemainingTf = new JTextField();
moneyRemainingTf.setEditable(false);
loyaltyCardBTN = new JButton("Scan Loyalty Card");
loyaltyCardBTN.addActionListener(controller);
loyaltyCardBTN.setActionCommand("SCAN-CARD");
panel.add(new JLabel("Cash"));
panel.add(balanceTf);
panel.add(new JLabel("Remaining Balance"));
panel.add(moneyRemainingTf);
panel.add(loyaltyCardBTN);
c.gridx = 1;
c.gridy = 0;
return panel;
}
private JPanel coinKeypad() {
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createTitledBorder("Coins"));
panel.setLayout(new GridLayout(3, 2, 5, 5));
JButton button = new JButton("5p");
button.addActionListener(e -> {
coinSlot.depositCoin(0.05);
balanceTf.setText(String.valueOf(coinSlot.getCoinsValue()));
});
panel.add(button);
button = new JButton("10p");
button.addActionListener(e -> {
coinSlot.depositCoin(0.10);
balanceTf.setText(String.valueOf(coinSlot.getCoinsValue()));
});
panel.add(button);
button = new JButton("20p");
button.addActionListener(e -> {
coinSlot.depositCoin(0.20);
balanceTf.setText(String.valueOf(coinSlot.getCoinsValue()));
});
panel.add(button);
button = new JButton("50p");
button.addActionListener(e -> {
coinSlot.depositCoin(0.50);
balanceTf.setText(String.valueOf(coinSlot.getCoinsValue()));
});
panel.add(button);
button = new JButton("£1");
button.addActionListener(e -> {
coinSlot.depositCoin(1);
balanceTf.setText(String.valueOf(coinSlot.getCoinsValue()));
});
panel.add(button);
button = new JButton("£2");
button.addActionListener(e -> {
coinSlot.depositCoin(2);
balanceTf.setText(String.valueOf(coinSlot.getCoinsValue()));
});
panel.add(button);
c.gridx = 0;
c.gridwidth = 2;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridy = 1;
return panel;
}
private JPanel createBody() {
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
panel.setLayout(new GridBagLayout());
panel.add(createRBButtonsPanel(), c);
panel.add(createInputPanel(), c);
panel.add(coinKeypad(), c);
return panel;
}
private JPanel createHeader() {
JPanel panel = new JPanel();
JLabel label = new JLabel("Vending Machine");
label.setFont(new Font("Helvetica Neue", 3, 48));
panel.add(label);
return panel;
}
private void addComponentsToPane(Container pane) {
JPanel header = createHeader();
JPanel body = createBody();
pane.add(header, BorderLayout.NORTH);
pane.add(body, BorderLayout.CENTER);
}
private void setLookAndFeel() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException
| UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
}
public void createAndShowGUI() {
JFrame frame = new JFrame("Vending Machine with a Four Dillabyte Crossfade");
addComponentsToPane(frame.getContentPane());
frame.setSize(800, 500);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
setLookAndFeel();
frame.setVisible(true);
frame.pack();
}
}
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JOptionPane;
public class VMController implements ActionListener {
private PaymentHandler handler = new PaymentHandler();
private double balance;
private String selectedRBText;
private GUI gui = new GUI(this);
VMController() {
this.balance = 0;
}
private double balanceToDouble() {
double balance = 0;
try {
return Double.valueOf(gui.getBalanceTF());
} catch (NumberFormatException ex) {
JOptionPane.showMessageDialog(null, "Invalid coin deposit.", "Enter numerical amount",
JOptionPane.ERROR_MESSAGE);
return balance;
}
}
private double productPriceToDouble() {
double productPrice = 0;
try {
return Double.valueOf(selectedRBText);
} catch (NullPointerException ex) {
return productPrice;
}
}
@Override
public void actionPerformed(ActionEvent e) {
String action = e.getActionCommand();
switch (action) {
case "COKE":
gui.deselectRadioButtons("COKE");
selectedRBText = gui.getCokeRbText().substring(gui.getCokeRbText().indexOf('£') + 1);
break;
case "LEMONADE":
gui.deselectRadioButtons("LEMONADE");
selectedRBText = gui.getLemonadeRbText().substring(gui.getLemonadeRbText().indexOf('£') + 1);
break;
case "TANGO":
gui.deselectRadioButtons("TANGO");
selectedRBText = gui.getTangoRbText().substring(gui.getTangoRbText().indexOf('£') + 1);
break;
case "WATER":
gui.deselectRadioButtons("WATER");
selectedRBText = gui.getWaterRbText().substring(gui.getWaterRbText().indexOf('£') + 1);
break;
case "PEPSI":
gui.deselectRadioButtons("PEPSI");
selectedRBText = gui.getPepsiRbText().substring(gui.getPepsiRbText().indexOf('£') + 1);
break;
case "SPRITE":
gui.deselectRadioButtons("SPRITE");
selectedRBText = gui.getSpriteRbText().substring(gui.getSpriteRbText().indexOf('£') + 1);
break;
case "PURCHASE":
if (!gui.isRBSelected())
JOptionPane.showMessageDialog(null, "Choose a product to purchase", "Select a product",
JOptionPane.ERROR_MESSAGE);
else {
if (handler.handlePayment(balanceToDouble(), productPriceToDouble()))
gui.setBalanceTF(String.valueOf(handler.getBalance()));
else
JOptionPane.showMessageDialog(null, "Balance is insufficient for product purchase.",
"Insufficient Funds", JOptionPane.ERROR_MESSAGE);
}
break;
case "CANCEL":
if (handler.getBalance() > 0) {
JOptionPane.showMessageDialog(null, "Order Cancelled.\nChange dispensed.\n£" + handler.getBalance());
}
JOptionPane.showMessageDialog(null, "Order Cancelled.");
System.exit(0);
break;
case "CLEAR":
gui.clearAllFields();
break;
case "SCAN-CARD":
System.out.println("scan card");
break;
default:
System.out.println("Action command not defined - \"" + action + "\"");
break;
}
}
}
简介
我将您的代码复制到我的 Eclipse IDE。缺少 class,所以我看不到您的 GUI 是什么样子。
所以我创建了这个 GUI。
我离开了会员卡按钮。
说明
Oracle 有一个有用的教程,Creating a GUI With Swing。跳过使用 NetBeans 学习 Swing IDE 部分。
我在创建 Swing 应用程序时使用 model-view-controller (MVC) 模式。顾名思义,您首先创建模型,然后是视图,然后是控制器。
应用程序模型由一个或多个普通Java getter/setter classes.
该视图由一个 JFrame
、一个或多个 JPanels
和 Swing 组件组成。
每个 Action
或 ActionListener
class 组成控制器。通常没有一个控制器可以“一统天下”。
型号
我创建的第一个模型 class 是 Item
class。 Item
class 包含名称和价格。价格以便士指定。使用 int 字段保存价格使计算更容易。我在显示价格时将便士换算成英镑。
我创建的第二个模型 class 是 VendingMachineModel
class。 VendingMachineModel
class 包含 java.util.List
项、java.util.List
硬币、int
余额字段和 int
更改字段。
查看
您的观点易于理解且结构合理,因此我在这里没有太多评论。我使用 ButtonGroup
将 JRadioButtons
限制为一个选择。
不要将所有 Swing 组件设为 class 字段。这会让您的代码的读者感到困惑。 Class 字段只能用于不止一种方法需要的字段。
在 class 中从最重要的方法到最不重要的方法对您的方法进行排序。这使代码更易于人们阅读和理解。
控制器
除了购买按钮,我对所有 JButtons
使用了 lambda 表达式。在我看来,ActionPerformed
方法对于 lambda 表达式来说太大了,所以我创建了一个 PurchaseListener
class.
代码
这是完整的可运行代码。我在 classes 内部制作了额外的 classes 这样我就可以 post 代码作为一个块。
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Font;
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.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.ButtonModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class VendingMachineExample implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new VendingMachineExample());
}
private ButtonGroup buttonGroup;
private JFrame frame;
private JTextField balanceField, changeField;
private final VendingMachineModel model;
public VendingMachineExample() {
this.model = new VendingMachineModel();
}
@Override
public void run() {
frame = new JFrame("Vending Machine with a Four Dillabyte Crossfade");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createHeaderPanel(), BorderLayout.NORTH);
frame.add(createBodyPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createHeaderPanel() {
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JLabel label = new JLabel("Vending Machine");
label.setFont(new Font("Helvetica Neue", 3, 48));
panel.add(label);
return panel;
}
private JPanel createBodyPanel() {
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
panel.setLayout(new FlowLayout());
panel.add(createVendingPanel());
panel.add(createDisplayPanel());
panel.add(createCoinKeypad());
return panel;
}
private JPanel createVendingPanel() {
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(0, 3, 5, 5));
panel.setBorder(BorderFactory.createTitledBorder("Select an item"));
buttonGroup = new ButtonGroup();
for (Item item : model.getItems()) {
double price = 0.01 * item.getPrice();
String text = item.getName() + ": £" + String.format("%.2f", price);
JRadioButton button = new JRadioButton(text);
text = item.getName() + ";;;" + item.getPrice();
button.setActionCommand(text);
buttonGroup.add(button);
panel.add(button);
}
JButton button = new JButton("Purchase");
button.addActionListener(new PurchaseListener(this, model));
panel.add(button);
button = new JButton("Clear");
button.addActionListener(event -> {
buttonGroup.clearSelection();
});
panel.add(button);
button = new JButton("Cancel");
button.addActionListener(event -> {
buttonGroup.clearSelection();
int amount = model.getBalance();
model.setBalance(0);
model.setChange(amount);
updateDisplayPanel();
});
panel.add(button);
return panel;
}
private JPanel createDisplayPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.add(new JLabel("Cash"));
balanceField = new JTextField(10);
balanceField.setEditable(false);
balanceField.setHorizontalAlignment(JTextField.CENTER);
panel.add(balanceField);
panel.add(new JLabel("Change"));
changeField = new JTextField(10);
changeField.setEditable(false);
changeField.setHorizontalAlignment(JTextField.CENTER);
panel.add(changeField);
updateDisplayPanel();
return panel;
}
public void updateDisplayPanel() {
double amount = 0.01 * model.getBalance();
String text = "£" + String.format("%.2f", amount);
balanceField.setText(text);
amount = 0.01 * model.getChange();
text = "£" + String.format("%.2f", amount);
changeField.setText(text);
}
private JPanel createCoinKeypad() {
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createTitledBorder("Coins"));
panel.setLayout(new GridLayout(0, 2, 5, 5));
for (Item item : model.getCoins()) {
JButton button = new JButton(item.getName());
button.setActionCommand(Integer.toString(item.getPrice()));
button.addActionListener(event -> {
model.setChange(0);
model.addCoin(item.getPrice());
updateDisplayPanel();
});
panel.add(button);
}
return panel;
}
public ButtonGroup getButtonGroup() {
return buttonGroup;
}
public JFrame getFrame() {
return frame;
}
public class PurchaseListener implements ActionListener {
private final VendingMachineExample view;
private final VendingMachineModel model;
public PurchaseListener(VendingMachineExample view, VendingMachineModel model) {
this.view = view;
this.model = model;
}
@Override
public void actionPerformed(ActionEvent event) {
ButtonModel buttonModel = view.getButtonGroup().getSelection();
if (buttonModel == null) {
JOptionPane.showMessageDialog(view.getFrame(),
"Please select an item", "Select an item",
JOptionPane.ERROR_MESSAGE);
} else {
String text = buttonModel.getActionCommand();
String[] parts = text.split(";;;");
String name = parts[0];
int amount = Integer.valueOf(parts[1]);
int balance = model.getBalance();
if (balance >= amount) {
displayPurchaseMessage(name, amount, balance);
} else {
displayDifferenceMessage(amount, balance);
}
}
}
private void displayPurchaseMessage(String name, int amount, int balance) {
String text;
text = "You purchased a " + name;
JOptionPane.showMessageDialog(view.getFrame(), text, "Item purchased",
JOptionPane.INFORMATION_MESSAGE);
model.purchaseProduct(amount);
int change = balance - amount;
model.setBalance(0);;
model.setChange(change);
view.updateDisplayPanel();
view.getButtonGroup().clearSelection();
}
private void displayDifferenceMessage(int amount, int balance) {
String text;
int difference = amount - balance;
double price = 0.01 * difference;
text = "Please deposit an additional £" +
String.format("%.2f", price);
JOptionPane.showMessageDialog(view.getFrame(), text, "Add Coins",
JOptionPane.ERROR_MESSAGE);
}
}
public class VendingMachineModel {
private int balance, change;
private final List<Item> coins, items;
public VendingMachineModel() {
this.balance = 0;
this.change = 0;
this.coins = new ArrayList<>();
this.coins.add(new Item("5p", 5));
this.coins.add(new Item("10p", 10));
this.coins.add(new Item("20p", 20));
this.coins.add(new Item("50p", 50));
this.coins.add(new Item("£1", 100));
this.coins.add(new Item("£2", 200));
this.items = new ArrayList<>();
this.items.add(new Item("Coke", 150));
this.items.add(new Item("Lemonade", 120));
this.items.add(new Item("Tango", 140));
this.items.add(new Item("Water", 100));
this.items.add(new Item("Pepsi", 130));
this.items.add(new Item("Sprite", 120));
}
public List<Item> getCoins() {
return coins;
}
public List<Item> getItems() {
return items;
}
public void addCoin(int amount) {
this.balance += amount;
}
public void purchaseProduct(int amount) {
this.balance -= amount;
}
public void setBalance(int balance) {
this.balance = balance;
}
public int getBalance() {
return balance;
}
public void setChange(int change) {
this.change = change;
}
public int getChange() {
return change;
}
}
public class Item {
private final int price;
private final String name;
public Item(String name, int price) {
this.name = name;
this.price = price;
}
public int getPrice() {
return price;
}
public String getName() {
return name;
}
}
}
我试图将以下代码的逻辑和 UI 设计分离到它们自己的 class 中,但我无法弄清楚如何调用链接到 swing 组件的某些方法我的 GUI class 在 Controller class.
我的问题是,当我在控制器 class 中创建 GUI class 的实例时,G[=25= 中的 JRadioButtons class 变量]只在GUI中的createRBButtonsPanel()方法中初始化。当我尝试调用控制器的 actionPerformed 方法中的 cokeRb.getText() 方法时,这会给我一个 NullPointerException,因为我没有调用导致它们被初始化的事件链。
我试图避免将它们全部设置为静态,因为我确信这样做不是最佳做法。
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class GUI {
private CoinAcceptor coinSlot = new CoinAcceptor();
private VMController controller;
GUI(VMController controller) {
this.controller = controller;
}
private JRadioButton cokeRb, lemonadeRb, tangoRb, waterRb, pepsiRb, spriteRb;
private JButton purchaseBtn , clearBtn, cancelBtn, loyaltyCardBTN;
private JTextField moneyRemainingTf, balanceTf;
private GridBagConstraints c = new GridBagConstraints();
//// Getters /////////////////////////////////
public String getMoneyTFText() { return this.moneyRemainingTf.getText(); }
public String getBalanceTF() { return balanceTf.getText(); }
public String getCokeRbText() { return this.cokeRb.getText(); }
public String getTangoRbText() { return this.tangoRb.getText(); }
public String getLemonadeRbText() { return this.lemonadeRb.getText(); }
public String getWaterRbText() { return this.waterRb.getText(); }
public String getPepsiRbText() { return this.pepsiRb.getText(); }
public String getSpriteRbText() { return this.spriteRb.getText(); }
//// Setters /////////////////////////////////
public void setBalanceTF(String text) { balanceTf.setText(text); }
public void setCokeRbText(String text) { this.cokeRb.setText(text); }
public void setLemonadeRbText(String text) { this.lemonadeRb.setText(text); }
public void setTangoRbText(String text) { this.tangoRb.setText(text); }
public void setWaterRbText(String text) { this.waterRb.setText(text); }
public void setPepsiRbText(String text) { this.pepsiRb.setText(text);}
public void setSpriteRbText(String text) { this.spriteRb.setText(text); }
public void deselectRadioButtons(String button) {
switch (button) {
case "COKE":
this.cokeRb.setSelected(true);
this.lemonadeRb.setSelected(false);
this.tangoRb.setSelected(false);
this.waterRb.setSelected(false);
this.pepsiRb.setSelected(false);
this.spriteRb.setSelected(false);
break;
case "LEMONADE":
this.cokeRb.setSelected(false);
this.lemonadeRb.setSelected(true);
this.tangoRb.setSelected(false);
this.waterRb.setSelected(false);
this.pepsiRb.setSelected(false);
this.spriteRb.setSelected(false);
break;
case "TANGO":
this.lemonadeRb.setSelected(false);
this.cokeRb.setSelected(false);
this.tangoRb.setSelected(true);
this.waterRb.setSelected(false);
this.pepsiRb.setSelected(false);
this.spriteRb.setSelected(false);
break;
case "WATER":
this.lemonadeRb.setSelected(false);
this.tangoRb.setSelected(false);
this.cokeRb.setSelected(false);
this.waterRb.setSelected(true);
this.pepsiRb.setSelected(false);
this.spriteRb.setSelected(false);
break;
case "PEPSI":
this.lemonadeRb.setSelected(false);
this.tangoRb.setSelected(false);
this.waterRb.setSelected(false);
this.cokeRb.setSelected(false);
this.pepsiRb.setSelected(true);
this.spriteRb.setSelected(false);
break;
case "SPRITE":
this.lemonadeRb.setSelected(false);
this.tangoRb.setSelected(false);
this.waterRb.setSelected(false);
this.pepsiRb.setSelected(false);
this.cokeRb.setSelected(false);
this.spriteRb.setSelected(true);
break;
default:
this.lemonadeRb.setSelected(false);
this.tangoRb.setSelected(false);
this.waterRb.setSelected(false);
this.pepsiRb.setSelected(false);
this.cokeRb.setSelected(false);
this.spriteRb.setSelected(false);
break;
}
}
public void clearAllFields() {
deselectRadioButtons("");
balanceTf.setText("");
this.moneyRemainingTf.setText("");
}
public boolean isRBSelected() {
JRadioButton[] radioButtons = new JRadioButton[] { cokeRb, lemonadeRb, tangoRb, waterRb, pepsiRb, spriteRb };
for (JRadioButton button : radioButtons) {
if (button.isSelected())
return true;
}
return false;
}
private JPanel createRBButtonsPanel() {
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(3, 6, 5, 5));
panel.setBorder(BorderFactory.createTitledBorder("Select and item"));
cokeRb = new JRadioButton("Coke : £1.50");
cokeRb.addActionListener(controller);
cokeRb.setActionCommand("COKE");
lemonadeRb = new JRadioButton("Lemonade : £1.20");
lemonadeRb.addActionListener(controller);
lemonadeRb.setActionCommand("LEMONADE");
tangoRb = new JRadioButton("Tango : £1.40");
tangoRb.addActionListener(controller);
tangoRb.setActionCommand("TANGO");
waterRb = new JRadioButton("Water : £1");
waterRb.addActionListener(controller);
waterRb.setActionCommand("WATER");
pepsiRb = new JRadioButton("Pepsi : £1.30");
pepsiRb.addActionListener(controller);
pepsiRb.setActionCommand("PEPSI");
spriteRb = new JRadioButton("Sprite : £1.20");
spriteRb.addActionListener(controller);
spriteRb.setActionCommand("SPRITE");
purchaseBtn = new JButton("PURCHASE");
purchaseBtn.addActionListener(controller);
clearBtn = new JButton("CLEAR");
clearBtn.addActionListener(controller);
cancelBtn = new JButton("CANCEL");
cancelBtn.addActionListener(controller);
panel.add(cokeRb);
panel.add(lemonadeRb);
panel.add(tangoRb);
panel.add(waterRb);
panel.add(pepsiRb);
panel.add(spriteRb);
panel.add(purchaseBtn);
panel.add(clearBtn);
panel.add(cancelBtn);
c.gridx = 0;
c.gridy = 0;
c.insets = new Insets(10, 10, 10, 10);
return panel;
}
private JPanel createInputPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
balanceTf = new JTextField();
// balanceTf.setForeground(Color.gray);
// balanceTf.addFocusListener(new FocusListener() {
//
// @Override
// public void focusGained(FocusEvent e) {
// if (balanceTf.getText().equals("2.50")) {
// balanceTf.setText("");
// balanceTf.setForeground(Color.black);
// }
// }
//
// @Override
// public void focusLost(FocusEvent e) {
// if (balanceTf.getText().isEmpty()) {
// balanceTf.setText("2.50");
// balanceTf.setForeground(Color.gray);
// }
// }
// });
moneyRemainingTf = new JTextField();
moneyRemainingTf.setEditable(false);
loyaltyCardBTN = new JButton("Scan Loyalty Card");
loyaltyCardBTN.addActionListener(controller);
loyaltyCardBTN.setActionCommand("SCAN-CARD");
panel.add(new JLabel("Cash"));
panel.add(balanceTf);
panel.add(new JLabel("Remaining Balance"));
panel.add(moneyRemainingTf);
panel.add(loyaltyCardBTN);
c.gridx = 1;
c.gridy = 0;
return panel;
}
private JPanel coinKeypad() {
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createTitledBorder("Coins"));
panel.setLayout(new GridLayout(3, 2, 5, 5));
JButton button = new JButton("5p");
button.addActionListener(e -> {
coinSlot.depositCoin(0.05);
balanceTf.setText(String.valueOf(coinSlot.getCoinsValue()));
});
panel.add(button);
button = new JButton("10p");
button.addActionListener(e -> {
coinSlot.depositCoin(0.10);
balanceTf.setText(String.valueOf(coinSlot.getCoinsValue()));
});
panel.add(button);
button = new JButton("20p");
button.addActionListener(e -> {
coinSlot.depositCoin(0.20);
balanceTf.setText(String.valueOf(coinSlot.getCoinsValue()));
});
panel.add(button);
button = new JButton("50p");
button.addActionListener(e -> {
coinSlot.depositCoin(0.50);
balanceTf.setText(String.valueOf(coinSlot.getCoinsValue()));
});
panel.add(button);
button = new JButton("£1");
button.addActionListener(e -> {
coinSlot.depositCoin(1);
balanceTf.setText(String.valueOf(coinSlot.getCoinsValue()));
});
panel.add(button);
button = new JButton("£2");
button.addActionListener(e -> {
coinSlot.depositCoin(2);
balanceTf.setText(String.valueOf(coinSlot.getCoinsValue()));
});
panel.add(button);
c.gridx = 0;
c.gridwidth = 2;
c.fill = GridBagConstraints.HORIZONTAL;
c.gridy = 1;
return panel;
}
private JPanel createBody() {
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
panel.setLayout(new GridBagLayout());
panel.add(createRBButtonsPanel(), c);
panel.add(createInputPanel(), c);
panel.add(coinKeypad(), c);
return panel;
}
private JPanel createHeader() {
JPanel panel = new JPanel();
JLabel label = new JLabel("Vending Machine");
label.setFont(new Font("Helvetica Neue", 3, 48));
panel.add(label);
return panel;
}
private void addComponentsToPane(Container pane) {
JPanel header = createHeader();
JPanel body = createBody();
pane.add(header, BorderLayout.NORTH);
pane.add(body, BorderLayout.CENTER);
}
private void setLookAndFeel() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException
| UnsupportedLookAndFeelException e) {
e.printStackTrace();
}
}
public void createAndShowGUI() {
JFrame frame = new JFrame("Vending Machine with a Four Dillabyte Crossfade");
addComponentsToPane(frame.getContentPane());
frame.setSize(800, 500);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
setLookAndFeel();
frame.setVisible(true);
frame.pack();
}
}
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JOptionPane;
public class VMController implements ActionListener {
private PaymentHandler handler = new PaymentHandler();
private double balance;
private String selectedRBText;
private GUI gui = new GUI(this);
VMController() {
this.balance = 0;
}
private double balanceToDouble() {
double balance = 0;
try {
return Double.valueOf(gui.getBalanceTF());
} catch (NumberFormatException ex) {
JOptionPane.showMessageDialog(null, "Invalid coin deposit.", "Enter numerical amount",
JOptionPane.ERROR_MESSAGE);
return balance;
}
}
private double productPriceToDouble() {
double productPrice = 0;
try {
return Double.valueOf(selectedRBText);
} catch (NullPointerException ex) {
return productPrice;
}
}
@Override
public void actionPerformed(ActionEvent e) {
String action = e.getActionCommand();
switch (action) {
case "COKE":
gui.deselectRadioButtons("COKE");
selectedRBText = gui.getCokeRbText().substring(gui.getCokeRbText().indexOf('£') + 1);
break;
case "LEMONADE":
gui.deselectRadioButtons("LEMONADE");
selectedRBText = gui.getLemonadeRbText().substring(gui.getLemonadeRbText().indexOf('£') + 1);
break;
case "TANGO":
gui.deselectRadioButtons("TANGO");
selectedRBText = gui.getTangoRbText().substring(gui.getTangoRbText().indexOf('£') + 1);
break;
case "WATER":
gui.deselectRadioButtons("WATER");
selectedRBText = gui.getWaterRbText().substring(gui.getWaterRbText().indexOf('£') + 1);
break;
case "PEPSI":
gui.deselectRadioButtons("PEPSI");
selectedRBText = gui.getPepsiRbText().substring(gui.getPepsiRbText().indexOf('£') + 1);
break;
case "SPRITE":
gui.deselectRadioButtons("SPRITE");
selectedRBText = gui.getSpriteRbText().substring(gui.getSpriteRbText().indexOf('£') + 1);
break;
case "PURCHASE":
if (!gui.isRBSelected())
JOptionPane.showMessageDialog(null, "Choose a product to purchase", "Select a product",
JOptionPane.ERROR_MESSAGE);
else {
if (handler.handlePayment(balanceToDouble(), productPriceToDouble()))
gui.setBalanceTF(String.valueOf(handler.getBalance()));
else
JOptionPane.showMessageDialog(null, "Balance is insufficient for product purchase.",
"Insufficient Funds", JOptionPane.ERROR_MESSAGE);
}
break;
case "CANCEL":
if (handler.getBalance() > 0) {
JOptionPane.showMessageDialog(null, "Order Cancelled.\nChange dispensed.\n£" + handler.getBalance());
}
JOptionPane.showMessageDialog(null, "Order Cancelled.");
System.exit(0);
break;
case "CLEAR":
gui.clearAllFields();
break;
case "SCAN-CARD":
System.out.println("scan card");
break;
default:
System.out.println("Action command not defined - \"" + action + "\"");
break;
}
}
}
简介
我将您的代码复制到我的 Eclipse IDE。缺少 class,所以我看不到您的 GUI 是什么样子。
所以我创建了这个 GUI。
我离开了会员卡按钮。
说明
Oracle 有一个有用的教程,Creating a GUI With Swing。跳过使用 NetBeans 学习 Swing IDE 部分。
我在创建 Swing 应用程序时使用 model-view-controller (MVC) 模式。顾名思义,您首先创建模型,然后是视图,然后是控制器。
应用程序模型由一个或多个普通Java getter/setter classes.
该视图由一个 JFrame
、一个或多个 JPanels
和 Swing 组件组成。
每个 Action
或 ActionListener
class 组成控制器。通常没有一个控制器可以“一统天下”。
型号
我创建的第一个模型 class 是 Item
class。 Item
class 包含名称和价格。价格以便士指定。使用 int 字段保存价格使计算更容易。我在显示价格时将便士换算成英镑。
我创建的第二个模型 class 是 VendingMachineModel
class。 VendingMachineModel
class 包含 java.util.List
项、java.util.List
硬币、int
余额字段和 int
更改字段。
查看
您的观点易于理解且结构合理,因此我在这里没有太多评论。我使用 ButtonGroup
将 JRadioButtons
限制为一个选择。
不要将所有 Swing 组件设为 class 字段。这会让您的代码的读者感到困惑。 Class 字段只能用于不止一种方法需要的字段。
在 class 中从最重要的方法到最不重要的方法对您的方法进行排序。这使代码更易于人们阅读和理解。
控制器
除了购买按钮,我对所有 JButtons
使用了 lambda 表达式。在我看来,ActionPerformed
方法对于 lambda 表达式来说太大了,所以我创建了一个 PurchaseListener
class.
代码
这是完整的可运行代码。我在 classes 内部制作了额外的 classes 这样我就可以 post 代码作为一个块。
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Font;
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.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.ButtonModel;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
public class VendingMachineExample implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new VendingMachineExample());
}
private ButtonGroup buttonGroup;
private JFrame frame;
private JTextField balanceField, changeField;
private final VendingMachineModel model;
public VendingMachineExample() {
this.model = new VendingMachineModel();
}
@Override
public void run() {
frame = new JFrame("Vending Machine with a Four Dillabyte Crossfade");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createHeaderPanel(), BorderLayout.NORTH);
frame.add(createBodyPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createHeaderPanel() {
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
JLabel label = new JLabel("Vending Machine");
label.setFont(new Font("Helvetica Neue", 3, 48));
panel.add(label);
return panel;
}
private JPanel createBodyPanel() {
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
panel.setLayout(new FlowLayout());
panel.add(createVendingPanel());
panel.add(createDisplayPanel());
panel.add(createCoinKeypad());
return panel;
}
private JPanel createVendingPanel() {
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(0, 3, 5, 5));
panel.setBorder(BorderFactory.createTitledBorder("Select an item"));
buttonGroup = new ButtonGroup();
for (Item item : model.getItems()) {
double price = 0.01 * item.getPrice();
String text = item.getName() + ": £" + String.format("%.2f", price);
JRadioButton button = new JRadioButton(text);
text = item.getName() + ";;;" + item.getPrice();
button.setActionCommand(text);
buttonGroup.add(button);
panel.add(button);
}
JButton button = new JButton("Purchase");
button.addActionListener(new PurchaseListener(this, model));
panel.add(button);
button = new JButton("Clear");
button.addActionListener(event -> {
buttonGroup.clearSelection();
});
panel.add(button);
button = new JButton("Cancel");
button.addActionListener(event -> {
buttonGroup.clearSelection();
int amount = model.getBalance();
model.setBalance(0);
model.setChange(amount);
updateDisplayPanel();
});
panel.add(button);
return panel;
}
private JPanel createDisplayPanel() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.add(new JLabel("Cash"));
balanceField = new JTextField(10);
balanceField.setEditable(false);
balanceField.setHorizontalAlignment(JTextField.CENTER);
panel.add(balanceField);
panel.add(new JLabel("Change"));
changeField = new JTextField(10);
changeField.setEditable(false);
changeField.setHorizontalAlignment(JTextField.CENTER);
panel.add(changeField);
updateDisplayPanel();
return panel;
}
public void updateDisplayPanel() {
double amount = 0.01 * model.getBalance();
String text = "£" + String.format("%.2f", amount);
balanceField.setText(text);
amount = 0.01 * model.getChange();
text = "£" + String.format("%.2f", amount);
changeField.setText(text);
}
private JPanel createCoinKeypad() {
JPanel panel = new JPanel();
panel.setBorder(BorderFactory.createTitledBorder("Coins"));
panel.setLayout(new GridLayout(0, 2, 5, 5));
for (Item item : model.getCoins()) {
JButton button = new JButton(item.getName());
button.setActionCommand(Integer.toString(item.getPrice()));
button.addActionListener(event -> {
model.setChange(0);
model.addCoin(item.getPrice());
updateDisplayPanel();
});
panel.add(button);
}
return panel;
}
public ButtonGroup getButtonGroup() {
return buttonGroup;
}
public JFrame getFrame() {
return frame;
}
public class PurchaseListener implements ActionListener {
private final VendingMachineExample view;
private final VendingMachineModel model;
public PurchaseListener(VendingMachineExample view, VendingMachineModel model) {
this.view = view;
this.model = model;
}
@Override
public void actionPerformed(ActionEvent event) {
ButtonModel buttonModel = view.getButtonGroup().getSelection();
if (buttonModel == null) {
JOptionPane.showMessageDialog(view.getFrame(),
"Please select an item", "Select an item",
JOptionPane.ERROR_MESSAGE);
} else {
String text = buttonModel.getActionCommand();
String[] parts = text.split(";;;");
String name = parts[0];
int amount = Integer.valueOf(parts[1]);
int balance = model.getBalance();
if (balance >= amount) {
displayPurchaseMessage(name, amount, balance);
} else {
displayDifferenceMessage(amount, balance);
}
}
}
private void displayPurchaseMessage(String name, int amount, int balance) {
String text;
text = "You purchased a " + name;
JOptionPane.showMessageDialog(view.getFrame(), text, "Item purchased",
JOptionPane.INFORMATION_MESSAGE);
model.purchaseProduct(amount);
int change = balance - amount;
model.setBalance(0);;
model.setChange(change);
view.updateDisplayPanel();
view.getButtonGroup().clearSelection();
}
private void displayDifferenceMessage(int amount, int balance) {
String text;
int difference = amount - balance;
double price = 0.01 * difference;
text = "Please deposit an additional £" +
String.format("%.2f", price);
JOptionPane.showMessageDialog(view.getFrame(), text, "Add Coins",
JOptionPane.ERROR_MESSAGE);
}
}
public class VendingMachineModel {
private int balance, change;
private final List<Item> coins, items;
public VendingMachineModel() {
this.balance = 0;
this.change = 0;
this.coins = new ArrayList<>();
this.coins.add(new Item("5p", 5));
this.coins.add(new Item("10p", 10));
this.coins.add(new Item("20p", 20));
this.coins.add(new Item("50p", 50));
this.coins.add(new Item("£1", 100));
this.coins.add(new Item("£2", 200));
this.items = new ArrayList<>();
this.items.add(new Item("Coke", 150));
this.items.add(new Item("Lemonade", 120));
this.items.add(new Item("Tango", 140));
this.items.add(new Item("Water", 100));
this.items.add(new Item("Pepsi", 130));
this.items.add(new Item("Sprite", 120));
}
public List<Item> getCoins() {
return coins;
}
public List<Item> getItems() {
return items;
}
public void addCoin(int amount) {
this.balance += amount;
}
public void purchaseProduct(int amount) {
this.balance -= amount;
}
public void setBalance(int balance) {
this.balance = balance;
}
public int getBalance() {
return balance;
}
public void setChange(int change) {
this.change = change;
}
public int getChange() {
return change;
}
}
public class Item {
private final int price;
private final String name;
public Item(String name, int price) {
this.name = name;
this.price = price;
}
public int getPrice() {
return price;
}
public String getName() {
return name;
}
}
}