JButton 未链接到 ActionLister
JButton not linked to ActionLister
public class OpenFrame extends JFrame implements ActionListener {
private static final int WIDTH = 500;
private static final int HEIGHT = 500;
private BudgetPlanner budgetPlanner;
private JFrame openFrame;
private JLabel welcomeMessage;
private JPanel openPanel = new JPanel();
private JPanel inputSpace;
private JButton loadButton = new JButton();
private JButton resetButton = new JButton();
private JButton addIncomeButton;
private JButton addExpenseButton;
private JButton viewExpenseButton;
private JButton viewIncomeButton;
private JTextField inputIncome;
public BudgetPlanner callBudgetPlanner() {
try {
budgetPlanner = new BudgetPlanner();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return budgetPlanner;
}
public OpenFrame() {
openFrame = new JFrame();
openFrame.setTitle("BudgetPlanner");
openFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
openFrame.setResizable(false);
openFrame.setLayout(null);
openFrame.setLocationRelativeTo(null);
openFrame.setSize(new Dimension(WIDTH, HEIGHT));
openFrame.getContentPane().setBackground(Color.GRAY);
openPanel();
openFrame.setContentPane(openPanel);
openFrame.setVisible(true);
}
public void openPanel() {
openPanel.setLayout(null);
welcomeMessage = new JLabel("Hello there! Welcome back. Choose an option below:");
welcomeMessage.setVerticalTextPosition(TOP);
welcomeMessage.setHorizontalTextPosition(SwingConstants.CENTER);
welcomeMessage.setBounds(50, 20, 500, 200);
welcomeMessage.setFont(new Font("Arial", Font.PLAIN, 17));
openPanel.setBounds(0, 0, 500, 500);
openPanel.add(welcomeMessage);
loadButton.setBounds(200, 170, 100, 50);
loadButton.setFont(new Font("Arial", Font.CENTER_BASELINE, 15));
loadButton.setText("Load Data");
loadButton.addActionListener(this);
loadButton.setFocusable(false);
resetButton.setBounds(200, 230, 100, 50);
resetButton.setFont(new Font("Arial", Font.CENTER_BASELINE, 15));
resetButton.setText("Reset Data");
resetButton.addActionListener(this);
resetButton.setFocusable(false);
openPanel.add(loadButton);
openPanel.add(resetButton);
}
public void plannerScreen() {
JPanel loadPanel = new JPanel();
openFrame.setContentPane(loadPanel);
loadPanel.setBounds(0, 0, 500, 500);
JLabel loadCompleteMessage = new JLabel("Welcome to Budget Planner App.");
loadCompleteMessage.setBounds(138, TOP, 500, 50);
loadCompleteMessage.setFont(new Font("Arial", Font.BOLD, 14));
loadPanel.add(loadCompleteMessage);
JLabel optionMessage = new JLabel("What would you like to do?");
optionMessage.setBounds(175, TOP + 40, 500, 10);
optionMessage.setFont(new Font("Arial", Font.ITALIC, 13));
loadPanel.add(optionMessage);
openButton();
loadPanel.add(addIncomeButton);
loadPanel.add(addExpenseButton);
loadPanel.add(viewIncomeButton);
loadPanel.add(viewExpenseButton);
}
public void openButton() {
addIncomeButton = new JButton("Add Income");
addIncomeButton.setBounds(180, 110, 140, 45);
addIncomeButton.setFont(new Font("Arial", Font.CENTER_BASELINE, 15));
addExpenseButton = new JButton("Add Expense");
addExpenseButton.setBounds(180, 160, 140, 45);
addExpenseButton.setFont(new Font("Arial", Font.CENTER_BASELINE, 15));
viewIncomeButton = new JButton("View Income");
viewIncomeButton.setBounds(180, 210, 140, 45);
viewIncomeButton.setFont(new Font("Arial", Font.CENTER_BASELINE, 15));
viewExpenseButton = new JButton("View Expense");
viewExpenseButton.setBounds(180, 260, 140, 45);
viewExpenseButton.setFont(new Font("Arial", Font.CENTER_BASELINE, 15));
addIncomeButton.addActionListener(new InputData());
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == loadButton) {
JOptionPane.showMessageDialog(null, "Loading Complete!");
plannerScreen();
}
if (e.getSource() == resetButton) {
JOptionPane.showMessageDialog(null, "Saved data erased.");
plannerScreen();
}
}
public class InputData implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
inputSpace = new JPanel();
inputSpace.setBounds(0, 300, 500, 200);
inputIncome = new JTextField("How much is your income?", 20);
openPanel.add(inputSpace);
inputSpace.add(inputIncome);
}
}
我的代码如上所示。问题是当我点击 addIncomeButton 时,它没有链接到我创建的 InputData 动作列表。单击 addIncomeButton 后,它不会执行任何操作。我该如何解决这个问题? ;-;
我的目标是单击按钮后,将出现一个 JTextField 并将添加到同一面板上。
任何帮助将不胜感激!非常感谢!
解决方案并不完美,您可以根据需要改进代码。按钮似乎正在响应点击以验证我在 actionPerformed
方法中添加了 System.out.println("Clicked")
语句。
package swingButton;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileNotFoundException;
import static javax.swing.SwingConstants.TOP;
public class OpenFrame extends JFrame implements ActionListener {
private static final int WIDTH = 500;
private static final int HEIGHT = 500;
// private BudgetPlanner budgetPlanner;
private JFrame openFrame;
private JLabel welcomeMessage;
private JPanel openPanel = new JPanel();
private JPanel inputSpace;
private JButton loadButton = new JButton();
private JButton resetButton = new JButton();
private JButton addIncomeButton;
private JButton addExpenseButton;
private JButton viewExpenseButton;
private JButton viewIncomeButton;
private JTextField inputIncome;
// public BudgetPlanner callBudgetPlanner() {
// try {
// budgetPlanner = new BudgetPlanner();
// } catch (FileNotFoundException e) {
// e.printStackTrace();
// }
// return budgetPlanner;
// }
public OpenFrame() {
openFrame = new JFrame();
openFrame.setTitle("BudgetPlanner");
openFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
openFrame.setResizable(false);
openFrame.setLayout(null);
openFrame.setLocationRelativeTo(null);
openFrame.setSize(new Dimension(WIDTH, HEIGHT));
openFrame.getContentPane().setBackground(Color.GRAY);
openPanel();
openFrame.setContentPane(openPanel);
openFrame.setVisible(true);
}
public void openPanel() {
openPanel.setLayout(null);
welcomeMessage = new JLabel("Hello there! Welcome back. Choose an option below:");
welcomeMessage.setVerticalTextPosition(TOP);
welcomeMessage.setHorizontalTextPosition(SwingConstants.CENTER);
welcomeMessage.setBounds(50, 20, 500, 200);
welcomeMessage.setFont(new Font("Arial", Font.PLAIN, 17));
openPanel.setBounds(0, 0, 500, 500);
openPanel.add(welcomeMessage);
loadButton.setBounds(200, 170, 100, 50);
loadButton.setFont(new Font("Arial", Font.CENTER_BASELINE, 15));
loadButton.setText("Load Data");
loadButton.addActionListener(this);
loadButton.setFocusable(false);
resetButton.setBounds(200, 230, 100, 50);
resetButton.setFont(new Font("Arial", Font.CENTER_BASELINE, 15));
resetButton.setText("Reset Data");
resetButton.addActionListener(this);
resetButton.setFocusable(false);
openPanel.add(loadButton);
openPanel.add(resetButton);
}
public void plannerScreen() {
JPanel loadPanel = new JPanel();
openFrame.setContentPane(loadPanel);
loadPanel.setBounds(0, 0, 500, 500);
JLabel loadCompleteMessage = new JLabel("Welcome to Budget Planner App.");
loadCompleteMessage.setBounds(138, TOP, 500, 50);
loadCompleteMessage.setFont(new Font("Arial", Font.BOLD, 14));
loadPanel.add(loadCompleteMessage);
JLabel optionMessage = new JLabel("What would you like to do?");
optionMessage.setBounds(175, TOP + 40, 500, 10);
optionMessage.setFont(new Font("Arial", Font.ITALIC, 13));
loadPanel.add(optionMessage);
openButton();
loadPanel.add(addIncomeButton);
loadPanel.add(addExpenseButton);
loadPanel.add(viewIncomeButton);
loadPanel.add(viewExpenseButton);
}
public void openButton() {
addIncomeButton = new JButton("Add Income");
addIncomeButton.setBounds(180, 110, 140, 45);
addIncomeButton.setFont(new Font("Arial", Font.CENTER_BASELINE, 15));
addExpenseButton = new JButton("Add Expense");
addExpenseButton.setBounds(180, 160, 140, 45);
addExpenseButton.setFont(new Font("Arial", Font.CENTER_BASELINE, 15));
viewIncomeButton = new JButton("View Income");
viewIncomeButton.setBounds(180, 210, 140, 45);
viewIncomeButton.setFont(new Font("Arial", Font.CENTER_BASELINE, 15));
viewExpenseButton = new JButton("View Expense");
viewExpenseButton.setBounds(180, 260, 140, 45);
viewExpenseButton.setFont(new Font("Arial", Font.CENTER_BASELINE, 15));
addIncomeButton.addActionListener(new InputData(this));
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == loadButton) {
JOptionPane.showMessageDialog(null, "Loading Complete!");
plannerScreen();
}
if (e.getSource() == resetButton) {
JOptionPane.showMessageDialog(null, "Saved data erased.");
plannerScreen();
}
}
public class InputData implements ActionListener {
JFrame parent;
public InputData(JFrame parent) {
this.parent = parent;
}
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Clicked");
JDialog dialog = new JDialog(parent);
JPanel inputSpace = new JPanel();
inputSpace.setLayout(new FlowLayout());
// inputSpace.setBounds(0, 300, 500, 200);
var incomeLabel = new JLabel("How much is your income?");
inputIncome = new JTextField("How much is your income?", 20);
inputSpace.add(incomeLabel);
inputSpace.add(inputIncome);
inputSpace.add(new JButton("OK"));
inputSpace.add(new JButton("Cancel"));
// openPanel.add(inputSpace);
// openFrame.removeAll();
// openFrame.setContentPane(inputSpace);
// openFrame.add(inputSpace);
// parent.setContentPane(inputSpace);
dialog.add(inputSpace);
System.out.println("Removing all");
dialog.setSize(500, 300);
dialog.setVisible(true);
}
}
public static void main(String... $) {
var of = new OpenFrame();
}
}
基本建议:
- 避免使用空布局和使用
setBounds(...)
来放置组件,因为这会导致 GUI 非常不灵活,虽然它们在一个平台上看起来不错,但在大多数其他平台或屏幕分辨率上看起来很糟糕,而且非常难以更新和维护。
- 用 Swing 交换或 show/hide 组件的最佳方法是使用 CardLayout。请查看 CardLayout tutorial.
- 不同的view应该用不同的JPanels表示
- 不同的观点应该有自己的观点class
- 将程序的数据和逻辑(程序的模型)从视图中分离到单独的 class 中,至少分离到模型和视图中 class 中,或者如果你愿意,一个 model-view-controller (MVC) 模式。
例如:
在下面的示例代码中,我在 SwapModel class:
中建立了模型和一些控制代码
import java.beans.*;
import java.util.*;
import javax.swing.event.*;
public class SwapModel {
public static String INCOME = "income";
private PropertyChangeSupport pcSupport = new SwingPropertyChangeSupport(this);
private SwapGuiMainPanel mainGui;
private Deque<String> cardStack = new LinkedList<>();
private double income = 0;
public SwapModel(SwapGuiMainPanel mainGui) {
this.mainGui = mainGui;
}
// when income is changed, update it and notify listeners.
public void addIncome(double value) {
double oldValue = this.income;
double newValue = this.income + value;
this.income = newValue;
pcSupport.firePropertyChange(INCOME, oldValue, newValue);
}
public double getIncome() {
return income;
}
// go to the next CardLayout view by calling show and pushing the view's constraint String onto the cardStack
public void swap(String name) {
mainGui.getCardLayout().show(mainGui.getHolderPanel(), name);
cardStack.push(name);
}
// go to the previous CardLayout view by popping the cardStack.
public String popCardStack() {
String name = null;
if (cardStack.size() > 1) {
cardStack.pop();
name = cardStack.peek();
mainGui.getCardLayout().show(mainGui.getHolderPanel(), name);
}
return name;
}
public void addPropertyChangeListener(String propName, PropertyChangeListener propListener) {
pcSupport.addPropertyChangeListener(propName, propListener);
}
}
这包含一个收入值,并通过使用 JavaBeans 属性 更改支持通知任何 class 想要注册有关收入值更改的侦听器的实体。它还通过控制 CardLayout 对象来帮助控制在什么时间显示哪个视图。
此示例程序代码的视图部分用于多个 JPanel,包括:
- 一个 WelcomePanel,显示欢迎文本并具有帮助用户加载其他 JPanel 的按钮。它目前只允许显示一个 JPanel,即 LoadPanel。
- 一个加载面板,它是一个允许用户添加或查看支出或收入的菜单。 JPanel 嵌套了多个布局管理器,包括 BoxLayout、GridLayout 和 GridBagLayout,以提供灵活的 GUI。 buttonFactory 方法允许创建具有统一外观的更大的 JButton。
- 允许用户向程序模型添加额外收入的 AddIncomePanel。
- 向用户显示当前收入的 ViewIncomePanel。 class 将使用 PropertyChangeListener 监听模型收入的变化 属性。
- 最后一个 SwapGuiMainPanel 来保存 CardLayout 和使用此布局的 holderPanel,并将所有内容放在一起:
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.*;
import java.beans.*;
import java.text.NumberFormat;
import javax.swing.*;
import javax.swing.border.Border;
@SuppressWarnings("serial")
public class SwapGuiMainPanel extends JPanel {
private CardLayout cardLayout = new CardLayout();
private JPanel holderPanel = new JPanel(cardLayout);
private SwapModel swapModel = new SwapModel(this);
private WelcomePanel welcomePanel = new WelcomePanel(swapModel);
private LoadPanel loadPanel = new LoadPanel(swapModel);
private AddIncomePanel addIncomePanel = new AddIncomePanel(swapModel);
private ViewIncomePanel viewIncomePanel = new ViewIncomePanel(swapModel);
public SwapGuiMainPanel() {
holderPanel.add(welcomePanel, WelcomePanel.NAME);
holderPanel.add(loadPanel, LoadPanel.NAME);
holderPanel.add(addIncomePanel, AddIncomePanel.NAME);
holderPanel.add(viewIncomePanel, ViewIncomePanel.NAME);
swapModel.addPropertyChangeListener(SwapModel.INCOME, new IncomeListener());
int gap = 10;
setBorder(BorderFactory.createEmptyBorder(gap, gap, gap, gap));
setLayout(new BorderLayout());
add(holderPanel);
}
private class IncomeListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
Double income = (Double) evt.getNewValue();
if (income != null) {
viewIncomePanel.setIncomeValue(income);
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
SwapGuiMainPanel mainPanel = new SwapGuiMainPanel();
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
public void swap(String constraint) {
cardLayout.show(holderPanel, constraint);
}
public CardLayout getCardLayout() {
return cardLayout;
}
public JPanel getHolderPanel() {
return holderPanel;
}
}
@SuppressWarnings("serial")
class WelcomePanel extends JPanel {
public static final String NAME = "welcome panel";
private static final String WELCOME_TEXT = "Hello and Welcome! Choose an Option Below:";
private SwapModel model;
public WelcomePanel(SwapModel model) {
this.model = model;
int gap = 20;
JPanel buttonPanel = new JPanel(new GridLayout(0, 1, gap, gap));
JButton loadButton = new JButton("Load Data");
loadButton.addActionListener(e -> loadData());
JButton resetButton = new JButton("Reset Data");
Font btnFont = loadButton.getFont().deriveFont(Font.BOLD, 16f);
loadButton.setFont(btnFont);
resetButton.setFont(btnFont);
buttonPanel.add(loadButton);
buttonPanel.add(resetButton);
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.insets = new Insets(gap, gap, gap, gap);
add(new JLabel(WELCOME_TEXT), gbc);
gbc.gridy++;
add(buttonPanel, gbc);
}
private void loadData() {
model.swap(LoadPanel.NAME);
}
}
@SuppressWarnings("serial")
class LoadPanel extends JPanel {
public static final String NAME = "load panel";
private SwapModel model;
public LoadPanel(SwapModel model) {
this.model = model;
JLabel title = new JLabel("Welcome to Budget Planner App");
title.setHorizontalAlignment(SwingConstants.CENTER);
title.setAlignmentX(JLabel.CENTER_ALIGNMENT);
title.setFont(title.getFont().deriveFont(Font.BOLD, 16f));
JLabel subTitle = new JLabel("What would you like to do?");
subTitle.setHorizontalAlignment(SwingConstants.CENTER);
subTitle.setAlignmentX(JLabel.CENTER_ALIGNMENT);
subTitle.setFont(subTitle.getFont().deriveFont(Font.ITALIC));
JPanel titlePanel = new JPanel();
titlePanel.setLayout(new BoxLayout(titlePanel, BoxLayout.PAGE_AXIS));
titlePanel.add(title);
titlePanel.add(subTitle);
titlePanel.add(Box.createVerticalStrut(30));
JPanel buttonPanel = new JPanel(new GridLayout(0, 1, 10, 10));
buttonPanel.add(buttonFactory("Add Income", e -> addIncome()));
buttonPanel.add(buttonFactory("Add Expense", null));
buttonPanel.add(buttonFactory("View Income", e -> viewIncome()));
buttonPanel.add(buttonFactory("View Expense", null));
JPanel wrapperPanel = new JPanel(new GridBagLayout());
wrapperPanel.add(buttonPanel);
int gap = 10;
setBorder(BorderFactory.createEmptyBorder(gap, gap, gap, gap));
setLayout(new BorderLayout());
add(titlePanel, BorderLayout.PAGE_START);
add(wrapperPanel);
}
private void addIncome() {
model.swap(AddIncomePanel.NAME);
}
private void viewIncome() {
model.swap(ViewIncomePanel.NAME);
}
private JButton buttonFactory(String text, ActionListener listener) {
JButton button = new JButton(text);
button.addActionListener(listener);
button.setFont(button.getFont().deriveFont(Font.BOLD, 16f));
Border originalBorder = button.getBorder();
int gap = 10;
Border innerBorder = BorderFactory.createEmptyBorder(gap, gap, gap, gap);
button.setBorder(BorderFactory.createCompoundBorder(originalBorder, innerBorder));
return button;
}
}
@SuppressWarnings("serial")
class AddIncomePanel extends JPanel {
public static final String NAME = "add income panel";
private SwapModel swapModel;
private JTextField incomeField = new JTextField(20);
private JButton submitButton = new JButton("Submit");
private JButton cancelButton = new JButton("Cancel");
public AddIncomePanel(SwapModel swapModel) {
this.swapModel = swapModel;
JPanel topPanel = new JPanel();
topPanel.add(new JLabel("Enter Income:"));
topPanel.add(incomeField);
submitButton.addActionListener(e -> submitData());
cancelButton.addActionListener(e -> cancel());
JPanel buttonPanel = new JPanel(new GridLayout(1, 0, 5, 5));
buttonPanel.add(submitButton);
buttonPanel.add(cancelButton);
JPanel innerPanel = new JPanel();
innerPanel.setLayout(new GridLayout(0, 1, 5, 5));
innerPanel.add(topPanel);
innerPanel.add(buttonPanel);
JButton backButton = new JButton("Back");
backButton.addActionListener(e -> back());
JPanel bottomButtonPanel = new JPanel();
bottomButtonPanel.add(backButton);
setLayout(new BorderLayout());
add(innerPanel, BorderLayout.PAGE_START);
add(bottomButtonPanel, BorderLayout.PAGE_END);
}
private void back() {
swapModel.popCardStack();
}
private void submitData() {
String text = incomeField.getText();
try {
double value = Double.parseDouble(text);
swapModel.addIncome(value);
back();
} catch (NumberFormatException e) {
// TODO: show error message
}
}
private void cancel() {
back();
}
}
@SuppressWarnings("serial")
class ViewIncomePanel extends JPanel {
public static final String NAME = "view income panel";
private SwapModel swapModel;
private JLabel incomeValue = new JLabel();
private NumberFormat format = NumberFormat.getCurrencyInstance();
public ViewIncomePanel(SwapModel swapModel) {
this.swapModel = swapModel;
JPanel topPanel = new JPanel();
topPanel.add(new JLabel("Income: "));
topPanel.add(incomeValue);
JButton backButton = new JButton("Back");
backButton.addActionListener(e -> back());
JPanel buttonPanel = new JPanel();
buttonPanel.add(backButton);
setLayout(new BorderLayout());
add(topPanel, BorderLayout.PAGE_START);
add(buttonPanel, BorderLayout.PAGE_END);
}
public void setIncomeValue(double income) {
incomeValue.setText(format.format(income));
}
private void back() {
swapModel.popCardStack();
}
}
public class OpenFrame extends JFrame implements ActionListener {
private static final int WIDTH = 500;
private static final int HEIGHT = 500;
private BudgetPlanner budgetPlanner;
private JFrame openFrame;
private JLabel welcomeMessage;
private JPanel openPanel = new JPanel();
private JPanel inputSpace;
private JButton loadButton = new JButton();
private JButton resetButton = new JButton();
private JButton addIncomeButton;
private JButton addExpenseButton;
private JButton viewExpenseButton;
private JButton viewIncomeButton;
private JTextField inputIncome;
public BudgetPlanner callBudgetPlanner() {
try {
budgetPlanner = new BudgetPlanner();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return budgetPlanner;
}
public OpenFrame() {
openFrame = new JFrame();
openFrame.setTitle("BudgetPlanner");
openFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
openFrame.setResizable(false);
openFrame.setLayout(null);
openFrame.setLocationRelativeTo(null);
openFrame.setSize(new Dimension(WIDTH, HEIGHT));
openFrame.getContentPane().setBackground(Color.GRAY);
openPanel();
openFrame.setContentPane(openPanel);
openFrame.setVisible(true);
}
public void openPanel() {
openPanel.setLayout(null);
welcomeMessage = new JLabel("Hello there! Welcome back. Choose an option below:");
welcomeMessage.setVerticalTextPosition(TOP);
welcomeMessage.setHorizontalTextPosition(SwingConstants.CENTER);
welcomeMessage.setBounds(50, 20, 500, 200);
welcomeMessage.setFont(new Font("Arial", Font.PLAIN, 17));
openPanel.setBounds(0, 0, 500, 500);
openPanel.add(welcomeMessage);
loadButton.setBounds(200, 170, 100, 50);
loadButton.setFont(new Font("Arial", Font.CENTER_BASELINE, 15));
loadButton.setText("Load Data");
loadButton.addActionListener(this);
loadButton.setFocusable(false);
resetButton.setBounds(200, 230, 100, 50);
resetButton.setFont(new Font("Arial", Font.CENTER_BASELINE, 15));
resetButton.setText("Reset Data");
resetButton.addActionListener(this);
resetButton.setFocusable(false);
openPanel.add(loadButton);
openPanel.add(resetButton);
}
public void plannerScreen() {
JPanel loadPanel = new JPanel();
openFrame.setContentPane(loadPanel);
loadPanel.setBounds(0, 0, 500, 500);
JLabel loadCompleteMessage = new JLabel("Welcome to Budget Planner App.");
loadCompleteMessage.setBounds(138, TOP, 500, 50);
loadCompleteMessage.setFont(new Font("Arial", Font.BOLD, 14));
loadPanel.add(loadCompleteMessage);
JLabel optionMessage = new JLabel("What would you like to do?");
optionMessage.setBounds(175, TOP + 40, 500, 10);
optionMessage.setFont(new Font("Arial", Font.ITALIC, 13));
loadPanel.add(optionMessage);
openButton();
loadPanel.add(addIncomeButton);
loadPanel.add(addExpenseButton);
loadPanel.add(viewIncomeButton);
loadPanel.add(viewExpenseButton);
}
public void openButton() {
addIncomeButton = new JButton("Add Income");
addIncomeButton.setBounds(180, 110, 140, 45);
addIncomeButton.setFont(new Font("Arial", Font.CENTER_BASELINE, 15));
addExpenseButton = new JButton("Add Expense");
addExpenseButton.setBounds(180, 160, 140, 45);
addExpenseButton.setFont(new Font("Arial", Font.CENTER_BASELINE, 15));
viewIncomeButton = new JButton("View Income");
viewIncomeButton.setBounds(180, 210, 140, 45);
viewIncomeButton.setFont(new Font("Arial", Font.CENTER_BASELINE, 15));
viewExpenseButton = new JButton("View Expense");
viewExpenseButton.setBounds(180, 260, 140, 45);
viewExpenseButton.setFont(new Font("Arial", Font.CENTER_BASELINE, 15));
addIncomeButton.addActionListener(new InputData());
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == loadButton) {
JOptionPane.showMessageDialog(null, "Loading Complete!");
plannerScreen();
}
if (e.getSource() == resetButton) {
JOptionPane.showMessageDialog(null, "Saved data erased.");
plannerScreen();
}
}
public class InputData implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
inputSpace = new JPanel();
inputSpace.setBounds(0, 300, 500, 200);
inputIncome = new JTextField("How much is your income?", 20);
openPanel.add(inputSpace);
inputSpace.add(inputIncome);
}
}
我的代码如上所示。问题是当我点击 addIncomeButton 时,它没有链接到我创建的 InputData 动作列表。单击 addIncomeButton 后,它不会执行任何操作。我该如何解决这个问题? ;-;
我的目标是单击按钮后,将出现一个 JTextField 并将添加到同一面板上。
任何帮助将不胜感激!非常感谢!
解决方案并不完美,您可以根据需要改进代码。按钮似乎正在响应点击以验证我在 actionPerformed
方法中添加了 System.out.println("Clicked")
语句。
package swingButton;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.FileNotFoundException;
import static javax.swing.SwingConstants.TOP;
public class OpenFrame extends JFrame implements ActionListener {
private static final int WIDTH = 500;
private static final int HEIGHT = 500;
// private BudgetPlanner budgetPlanner;
private JFrame openFrame;
private JLabel welcomeMessage;
private JPanel openPanel = new JPanel();
private JPanel inputSpace;
private JButton loadButton = new JButton();
private JButton resetButton = new JButton();
private JButton addIncomeButton;
private JButton addExpenseButton;
private JButton viewExpenseButton;
private JButton viewIncomeButton;
private JTextField inputIncome;
// public BudgetPlanner callBudgetPlanner() {
// try {
// budgetPlanner = new BudgetPlanner();
// } catch (FileNotFoundException e) {
// e.printStackTrace();
// }
// return budgetPlanner;
// }
public OpenFrame() {
openFrame = new JFrame();
openFrame.setTitle("BudgetPlanner");
openFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
openFrame.setResizable(false);
openFrame.setLayout(null);
openFrame.setLocationRelativeTo(null);
openFrame.setSize(new Dimension(WIDTH, HEIGHT));
openFrame.getContentPane().setBackground(Color.GRAY);
openPanel();
openFrame.setContentPane(openPanel);
openFrame.setVisible(true);
}
public void openPanel() {
openPanel.setLayout(null);
welcomeMessage = new JLabel("Hello there! Welcome back. Choose an option below:");
welcomeMessage.setVerticalTextPosition(TOP);
welcomeMessage.setHorizontalTextPosition(SwingConstants.CENTER);
welcomeMessage.setBounds(50, 20, 500, 200);
welcomeMessage.setFont(new Font("Arial", Font.PLAIN, 17));
openPanel.setBounds(0, 0, 500, 500);
openPanel.add(welcomeMessage);
loadButton.setBounds(200, 170, 100, 50);
loadButton.setFont(new Font("Arial", Font.CENTER_BASELINE, 15));
loadButton.setText("Load Data");
loadButton.addActionListener(this);
loadButton.setFocusable(false);
resetButton.setBounds(200, 230, 100, 50);
resetButton.setFont(new Font("Arial", Font.CENTER_BASELINE, 15));
resetButton.setText("Reset Data");
resetButton.addActionListener(this);
resetButton.setFocusable(false);
openPanel.add(loadButton);
openPanel.add(resetButton);
}
public void plannerScreen() {
JPanel loadPanel = new JPanel();
openFrame.setContentPane(loadPanel);
loadPanel.setBounds(0, 0, 500, 500);
JLabel loadCompleteMessage = new JLabel("Welcome to Budget Planner App.");
loadCompleteMessage.setBounds(138, TOP, 500, 50);
loadCompleteMessage.setFont(new Font("Arial", Font.BOLD, 14));
loadPanel.add(loadCompleteMessage);
JLabel optionMessage = new JLabel("What would you like to do?");
optionMessage.setBounds(175, TOP + 40, 500, 10);
optionMessage.setFont(new Font("Arial", Font.ITALIC, 13));
loadPanel.add(optionMessage);
openButton();
loadPanel.add(addIncomeButton);
loadPanel.add(addExpenseButton);
loadPanel.add(viewIncomeButton);
loadPanel.add(viewExpenseButton);
}
public void openButton() {
addIncomeButton = new JButton("Add Income");
addIncomeButton.setBounds(180, 110, 140, 45);
addIncomeButton.setFont(new Font("Arial", Font.CENTER_BASELINE, 15));
addExpenseButton = new JButton("Add Expense");
addExpenseButton.setBounds(180, 160, 140, 45);
addExpenseButton.setFont(new Font("Arial", Font.CENTER_BASELINE, 15));
viewIncomeButton = new JButton("View Income");
viewIncomeButton.setBounds(180, 210, 140, 45);
viewIncomeButton.setFont(new Font("Arial", Font.CENTER_BASELINE, 15));
viewExpenseButton = new JButton("View Expense");
viewExpenseButton.setBounds(180, 260, 140, 45);
viewExpenseButton.setFont(new Font("Arial", Font.CENTER_BASELINE, 15));
addIncomeButton.addActionListener(new InputData(this));
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == loadButton) {
JOptionPane.showMessageDialog(null, "Loading Complete!");
plannerScreen();
}
if (e.getSource() == resetButton) {
JOptionPane.showMessageDialog(null, "Saved data erased.");
plannerScreen();
}
}
public class InputData implements ActionListener {
JFrame parent;
public InputData(JFrame parent) {
this.parent = parent;
}
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("Clicked");
JDialog dialog = new JDialog(parent);
JPanel inputSpace = new JPanel();
inputSpace.setLayout(new FlowLayout());
// inputSpace.setBounds(0, 300, 500, 200);
var incomeLabel = new JLabel("How much is your income?");
inputIncome = new JTextField("How much is your income?", 20);
inputSpace.add(incomeLabel);
inputSpace.add(inputIncome);
inputSpace.add(new JButton("OK"));
inputSpace.add(new JButton("Cancel"));
// openPanel.add(inputSpace);
// openFrame.removeAll();
// openFrame.setContentPane(inputSpace);
// openFrame.add(inputSpace);
// parent.setContentPane(inputSpace);
dialog.add(inputSpace);
System.out.println("Removing all");
dialog.setSize(500, 300);
dialog.setVisible(true);
}
}
public static void main(String... $) {
var of = new OpenFrame();
}
}
基本建议:
- 避免使用空布局和使用
setBounds(...)
来放置组件,因为这会导致 GUI 非常不灵活,虽然它们在一个平台上看起来不错,但在大多数其他平台或屏幕分辨率上看起来很糟糕,而且非常难以更新和维护。 - 用 Swing 交换或 show/hide 组件的最佳方法是使用 CardLayout。请查看 CardLayout tutorial.
- 不同的view应该用不同的JPanels表示
- 不同的观点应该有自己的观点class
- 将程序的数据和逻辑(程序的模型)从视图中分离到单独的 class 中,至少分离到模型和视图中 class 中,或者如果你愿意,一个 model-view-controller (MVC) 模式。
例如:
在下面的示例代码中,我在 SwapModel class:
中建立了模型和一些控制代码import java.beans.*;
import java.util.*;
import javax.swing.event.*;
public class SwapModel {
public static String INCOME = "income";
private PropertyChangeSupport pcSupport = new SwingPropertyChangeSupport(this);
private SwapGuiMainPanel mainGui;
private Deque<String> cardStack = new LinkedList<>();
private double income = 0;
public SwapModel(SwapGuiMainPanel mainGui) {
this.mainGui = mainGui;
}
// when income is changed, update it and notify listeners.
public void addIncome(double value) {
double oldValue = this.income;
double newValue = this.income + value;
this.income = newValue;
pcSupport.firePropertyChange(INCOME, oldValue, newValue);
}
public double getIncome() {
return income;
}
// go to the next CardLayout view by calling show and pushing the view's constraint String onto the cardStack
public void swap(String name) {
mainGui.getCardLayout().show(mainGui.getHolderPanel(), name);
cardStack.push(name);
}
// go to the previous CardLayout view by popping the cardStack.
public String popCardStack() {
String name = null;
if (cardStack.size() > 1) {
cardStack.pop();
name = cardStack.peek();
mainGui.getCardLayout().show(mainGui.getHolderPanel(), name);
}
return name;
}
public void addPropertyChangeListener(String propName, PropertyChangeListener propListener) {
pcSupport.addPropertyChangeListener(propName, propListener);
}
}
这包含一个收入值,并通过使用 JavaBeans 属性 更改支持通知任何 class 想要注册有关收入值更改的侦听器的实体。它还通过控制 CardLayout 对象来帮助控制在什么时间显示哪个视图。
此示例程序代码的视图部分用于多个 JPanel,包括:
- 一个 WelcomePanel,显示欢迎文本并具有帮助用户加载其他 JPanel 的按钮。它目前只允许显示一个 JPanel,即 LoadPanel。
- 一个加载面板,它是一个允许用户添加或查看支出或收入的菜单。 JPanel 嵌套了多个布局管理器,包括 BoxLayout、GridLayout 和 GridBagLayout,以提供灵活的 GUI。 buttonFactory 方法允许创建具有统一外观的更大的 JButton。
- 允许用户向程序模型添加额外收入的 AddIncomePanel。
- 向用户显示当前收入的 ViewIncomePanel。 class 将使用 PropertyChangeListener 监听模型收入的变化 属性。
- 最后一个 SwapGuiMainPanel 来保存 CardLayout 和使用此布局的 holderPanel,并将所有内容放在一起:
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.event.*;
import java.beans.*;
import java.text.NumberFormat;
import javax.swing.*;
import javax.swing.border.Border;
@SuppressWarnings("serial")
public class SwapGuiMainPanel extends JPanel {
private CardLayout cardLayout = new CardLayout();
private JPanel holderPanel = new JPanel(cardLayout);
private SwapModel swapModel = new SwapModel(this);
private WelcomePanel welcomePanel = new WelcomePanel(swapModel);
private LoadPanel loadPanel = new LoadPanel(swapModel);
private AddIncomePanel addIncomePanel = new AddIncomePanel(swapModel);
private ViewIncomePanel viewIncomePanel = new ViewIncomePanel(swapModel);
public SwapGuiMainPanel() {
holderPanel.add(welcomePanel, WelcomePanel.NAME);
holderPanel.add(loadPanel, LoadPanel.NAME);
holderPanel.add(addIncomePanel, AddIncomePanel.NAME);
holderPanel.add(viewIncomePanel, ViewIncomePanel.NAME);
swapModel.addPropertyChangeListener(SwapModel.INCOME, new IncomeListener());
int gap = 10;
setBorder(BorderFactory.createEmptyBorder(gap, gap, gap, gap));
setLayout(new BorderLayout());
add(holderPanel);
}
private class IncomeListener implements PropertyChangeListener {
@Override
public void propertyChange(PropertyChangeEvent evt) {
Double income = (Double) evt.getNewValue();
if (income != null) {
viewIncomePanel.setIncomeValue(income);
}
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
SwapGuiMainPanel mainPanel = new SwapGuiMainPanel();
JFrame frame = new JFrame("GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
public void swap(String constraint) {
cardLayout.show(holderPanel, constraint);
}
public CardLayout getCardLayout() {
return cardLayout;
}
public JPanel getHolderPanel() {
return holderPanel;
}
}
@SuppressWarnings("serial")
class WelcomePanel extends JPanel {
public static final String NAME = "welcome panel";
private static final String WELCOME_TEXT = "Hello and Welcome! Choose an Option Below:";
private SwapModel model;
public WelcomePanel(SwapModel model) {
this.model = model;
int gap = 20;
JPanel buttonPanel = new JPanel(new GridLayout(0, 1, gap, gap));
JButton loadButton = new JButton("Load Data");
loadButton.addActionListener(e -> loadData());
JButton resetButton = new JButton("Reset Data");
Font btnFont = loadButton.getFont().deriveFont(Font.BOLD, 16f);
loadButton.setFont(btnFont);
resetButton.setFont(btnFont);
buttonPanel.add(loadButton);
buttonPanel.add(resetButton);
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.insets = new Insets(gap, gap, gap, gap);
add(new JLabel(WELCOME_TEXT), gbc);
gbc.gridy++;
add(buttonPanel, gbc);
}
private void loadData() {
model.swap(LoadPanel.NAME);
}
}
@SuppressWarnings("serial")
class LoadPanel extends JPanel {
public static final String NAME = "load panel";
private SwapModel model;
public LoadPanel(SwapModel model) {
this.model = model;
JLabel title = new JLabel("Welcome to Budget Planner App");
title.setHorizontalAlignment(SwingConstants.CENTER);
title.setAlignmentX(JLabel.CENTER_ALIGNMENT);
title.setFont(title.getFont().deriveFont(Font.BOLD, 16f));
JLabel subTitle = new JLabel("What would you like to do?");
subTitle.setHorizontalAlignment(SwingConstants.CENTER);
subTitle.setAlignmentX(JLabel.CENTER_ALIGNMENT);
subTitle.setFont(subTitle.getFont().deriveFont(Font.ITALIC));
JPanel titlePanel = new JPanel();
titlePanel.setLayout(new BoxLayout(titlePanel, BoxLayout.PAGE_AXIS));
titlePanel.add(title);
titlePanel.add(subTitle);
titlePanel.add(Box.createVerticalStrut(30));
JPanel buttonPanel = new JPanel(new GridLayout(0, 1, 10, 10));
buttonPanel.add(buttonFactory("Add Income", e -> addIncome()));
buttonPanel.add(buttonFactory("Add Expense", null));
buttonPanel.add(buttonFactory("View Income", e -> viewIncome()));
buttonPanel.add(buttonFactory("View Expense", null));
JPanel wrapperPanel = new JPanel(new GridBagLayout());
wrapperPanel.add(buttonPanel);
int gap = 10;
setBorder(BorderFactory.createEmptyBorder(gap, gap, gap, gap));
setLayout(new BorderLayout());
add(titlePanel, BorderLayout.PAGE_START);
add(wrapperPanel);
}
private void addIncome() {
model.swap(AddIncomePanel.NAME);
}
private void viewIncome() {
model.swap(ViewIncomePanel.NAME);
}
private JButton buttonFactory(String text, ActionListener listener) {
JButton button = new JButton(text);
button.addActionListener(listener);
button.setFont(button.getFont().deriveFont(Font.BOLD, 16f));
Border originalBorder = button.getBorder();
int gap = 10;
Border innerBorder = BorderFactory.createEmptyBorder(gap, gap, gap, gap);
button.setBorder(BorderFactory.createCompoundBorder(originalBorder, innerBorder));
return button;
}
}
@SuppressWarnings("serial")
class AddIncomePanel extends JPanel {
public static final String NAME = "add income panel";
private SwapModel swapModel;
private JTextField incomeField = new JTextField(20);
private JButton submitButton = new JButton("Submit");
private JButton cancelButton = new JButton("Cancel");
public AddIncomePanel(SwapModel swapModel) {
this.swapModel = swapModel;
JPanel topPanel = new JPanel();
topPanel.add(new JLabel("Enter Income:"));
topPanel.add(incomeField);
submitButton.addActionListener(e -> submitData());
cancelButton.addActionListener(e -> cancel());
JPanel buttonPanel = new JPanel(new GridLayout(1, 0, 5, 5));
buttonPanel.add(submitButton);
buttonPanel.add(cancelButton);
JPanel innerPanel = new JPanel();
innerPanel.setLayout(new GridLayout(0, 1, 5, 5));
innerPanel.add(topPanel);
innerPanel.add(buttonPanel);
JButton backButton = new JButton("Back");
backButton.addActionListener(e -> back());
JPanel bottomButtonPanel = new JPanel();
bottomButtonPanel.add(backButton);
setLayout(new BorderLayout());
add(innerPanel, BorderLayout.PAGE_START);
add(bottomButtonPanel, BorderLayout.PAGE_END);
}
private void back() {
swapModel.popCardStack();
}
private void submitData() {
String text = incomeField.getText();
try {
double value = Double.parseDouble(text);
swapModel.addIncome(value);
back();
} catch (NumberFormatException e) {
// TODO: show error message
}
}
private void cancel() {
back();
}
}
@SuppressWarnings("serial")
class ViewIncomePanel extends JPanel {
public static final String NAME = "view income panel";
private SwapModel swapModel;
private JLabel incomeValue = new JLabel();
private NumberFormat format = NumberFormat.getCurrencyInstance();
public ViewIncomePanel(SwapModel swapModel) {
this.swapModel = swapModel;
JPanel topPanel = new JPanel();
topPanel.add(new JLabel("Income: "));
topPanel.add(incomeValue);
JButton backButton = new JButton("Back");
backButton.addActionListener(e -> back());
JPanel buttonPanel = new JPanel();
buttonPanel.add(backButton);
setLayout(new BorderLayout());
add(topPanel, BorderLayout.PAGE_START);
add(buttonPanel, BorderLayout.PAGE_END);
}
public void setIncomeValue(double income) {
incomeValue.setText(format.format(income));
}
private void back() {
swapModel.popCardStack();
}
}