Java GUI 开发:从 JPanel 扩展类及相关技术
1. 从 JPanel 扩展类的概念
在开发 GUI 应用程序时,为了避免将大量组件和事件监听器的代码捆绑在一个类中导致类变得庞大和复杂,我们可以采用从
JPanel
类扩展类的方法。通过编写一个继承自
JPanel
类的类,我们可以创建自定义的面板组件,该组件可以包含其他组件及其相关代码。
2. Brandi’s Bagel House 应用案例
Brandi’s Bagel House 是一家提供百吉饼和咖啡配送服务的店铺,顾客可以电话订购不同类型的百吉饼、配料和咖啡。店主需要一个“订单计算器”应用程序,让员工能够计算订单价格,并显示小计、6% 的销售税和订单总价。
2.1 价格列表
| 商品类型 | 具体商品 | 价格 |
|---|---|---|
| 百吉饼 | 白百吉饼 | $1.25 |
| 百吉饼 | 全麦百吉饼 | $1.50 |
| 配料 | 奶油奶酪 | $0.50 |
| 配料 | 黄油 | $0.25 |
| 配料 | 桃果冻 | $0.75 |
| 配料 | 蓝莓果酱 | $0.75 |
| 咖啡 | 普通咖啡 | $1.25 |
| 咖啡 | 无咖啡因咖啡 | $1.25 |
| 咖啡 | 卡布奇诺 | $2.00 |
2.2 窗口布局
应用程序窗口采用
BorderLayout
布局管理器,各区域组件分布如下:
- 北部区域:显示“Welcome to Brandi’s Bagel House”的标签。
- 西部区域:百吉饼类型的单选按钮。
- 中部区域:配料的复选框。
- 东部区域:咖啡选择的单选按钮。
- 南部区域:“Calculate”和“Exit”按钮。
为了构建这个窗口,我们创建了以下从
JPanel
扩展的专门面板类:
-
GreetingsPanel
:包含显示在窗口北部区域的标签。
-
BagelPanel
:包含百吉饼类型的单选按钮。
-
ToppingPanel
:包含配料的复选框。
-
CoffeePanel
:包含咖啡选择的单选按钮。
3. 各面板类的实现
3.1 GreetingPanel 类
import javax.swing.*;
/**
* The GreetingPanel class displays a greeting in a panel.
*/
public class GreetingPanel extends JPanel {
private JLabel greeting; // To display a greeting
/**
* Constructor
*/
public GreetingPanel() {
// Create the label.
greeting = new JLabel("Welcome to Brandi's Bagel House");
// Add the label to this panel.
add(greeting);
}
}
当创建这个类的实例时,会创建一个显示“Welcome to Brandi’s Bagel House”标签的
JPanel
组件。
3.2 BagelPanel 类
import javax.swing.*;
import java.awt.*;
/**
* The BagelPanel class allows the user to select either
* a white or whole wheat bagel.
*/
public class BagelPanel extends JPanel {
// The following constants are used to indicate
// the cost of each type of bagel.
public final double WHITE_BAGEL = 1.25;
public final double WHEAT_BAGEL = 1.50;
private JRadioButton whiteBagel; // To select white
private JRadioButton wheatBagel; // To select wheat
private ButtonGroup bg; // Radio button group
/**
* Constructor
*/
public BagelPanel() {
// Create a GridLayout manager with
// two rows and one column.
setLayout(new GridLayout(2, 1));
// Create the radio buttons.
whiteBagel = new JRadioButton("White", true);
wheatBagel = new JRadioButton("Wheat");
// Group the radio buttons.
bg = new ButtonGroup();
bg.add(whiteBagel);
bg.add(wheatBagel);
// Add a border around the panel.
setBorder(BorderFactory.createTitledBorder("Bagel"));
// Add the radio buttons to the panel.
add(whiteBagel);
add(wheatBagel);
}
/**
* getBagelCost method
* @return The cost of the selected bagel.
*/
public double getBagelCost() {
double bagelCost = 0.0;
if (whiteBagel.isSelected())
bagelCost = WHITE_BAGEL;
else
bagelCost = WHEAT_BAGEL;
return bagelCost;
}
}
该类使用
GridLayout
布局管理器,默认选择白百吉饼。
getBagelCost
方法用于返回所选百吉饼的成本。
3.3 ToppingPanel 类
import javax.swing.*;
import java.awt.*;
/**
* The ToppingPanel class allows the user to select
* the toppings for the bagel.
*/
public class ToppingPanel extends JPanel {
// The following constants are used to indicate
// the cost of toppings.
public final double CREAM_CHEESE = 0.50;
public final double BUTTER = 0.25;
public final double PEACH_JELLY = 0.75;
public final double BLUEBERRY_JAM = 0.75;
private JCheckBox creamCheese; // To select cream cheese
private JCheckBox butter; // To select butter
private JCheckBox peachJelly; // To select peach jelly
private JCheckBox blueberryJam; // To select blueberry jam
/**
* Constructor
*/
public ToppingPanel() {
// Create a GridLayout manager with
// four rows and one column.
setLayout(new GridLayout(4, 1));
// Create the check boxes.
creamCheese = new JCheckBox("Cream cheese");
butter = new JCheckBox("Butter");
peachJelly = new JCheckBox("Peach jelly");
blueberryJam = new JCheckBox("Blueberry jam");
// Add a border around the panel.
setBorder(BorderFactory.createTitledBorder("Toppings"));
// Add the check boxes to the panel.
add(creamCheese);
add(butter);
add(peachJelly);
add(blueberryJam);
}
/**
* getToppingCost method
* @return The cost of the selected toppings.
*/
public double getToppingCost() {
double toppingCost = 0.0;
if (creamCheese.isSelected())
toppingCost += CREAM_CHEESE;
if (butter.isSelected())
toppingCost += BUTTER;
if (peachJelly.isSelected())
toppingCost += PEACH_JELLY;
if (blueberryJam.isSelected())
toppingCost += BLUEBERRY_JAM;
return toppingCost;
}
}
该类同样使用
GridLayout
布局管理器,
getToppingCost
方法用于返回所选配料的总成本。
3.4 CoffeePanel 类
import javax.swing.*;
import java.awt.*;
/**
* The CoffeePanel class allows the user to select coffee.
*/
public class CoffeePanel extends JPanel {
// The following constants are used to indicate
// the cost of coffee.
public final double NO_COFFEE = 0.0;
public final double REGULAR_COFFEE = 1.25;
public final double DECAF_COFFEE = 1.25;
public final double CAPPUCCINO = 2.00;
private JRadioButton noCoffee; // To select no coffee
private JRadioButton regularCoffee; // To select regular coffee
private JRadioButton decafCoffee; // To select decaf
private JRadioButton cappuccino; // To select cappuccino
private ButtonGroup bg; // Radio button group
/**
* Constructor
*/
public CoffeePanel() {
// Create a GridLayout manager with
// four rows and one column.
setLayout(new GridLayout(4, 1));
// Create the radio buttons.
noCoffee = new JRadioButton("None");
regularCoffee = new JRadioButton("Regular coffee", true);
decafCoffee = new JRadioButton("Decaf coffee");
cappuccino = new JRadioButton("Cappuccino");
// Group the radio buttons.
bg = new ButtonGroup();
bg.add(noCoffee);
bg.add(regularCoffee);
bg.add(decafCoffee);
bg.add(cappuccino);
// Add a border around the panel.
setBorder(BorderFactory.createTitledBorder("Coffee"));
// Add the radio buttons to the panel.
add(noCoffee);
add(regularCoffee);
add(decafCoffee);
add(cappuccino);
}
/**
* getCoffeeCost method
* @return The cost of the selected coffee.
*/
public double getCoffeeCost() {
double coffeeCost = 0.0;
if (noCoffee.isSelected())
coffeeCost = NO_COFFEE;
else if (regularCoffee.isSelected())
coffeeCost = REGULAR_COFFEE;
else if (decafCoffee.isSelected())
coffeeCost = DECAF_COFFEE;
else if (cappuccino.isSelected())
coffeeCost = CAPPUCCINO;
return coffeeCost;
}
}
该类使用
GridLayout
布局管理器,默认选择普通咖啡。
getCoffeeCost
方法用于返回所选咖啡的成本。
4. 整合各面板类
最后,我们创建一个
OrderCalculatorGUI
类来构建应用程序窗口,并添加“Calculate”和“Exit”按钮。
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
/**
* The OrderCalculatorGUI class creates the GUI for the
* Brandi's Bagel House application.
*/
public class OrderCalculatorGUI extends JFrame {
private BagelPanel bagels; // Bagel panel
private ToppingPanel toppings; // Topping panel
private CoffeePanel coffee; // Coffee panel
private GreetingPanel banner; // To display a greeting
private JPanel buttonPanel; // To hold the buttons
private JButton calcButton; // To calculate the cost
private JButton exitButton; // To exit the application
private final double TAX_RATE = 0.06; // Sales tax rate
/**
* Constructor
*/
public OrderCalculatorGUI() {
// Display a title.
setTitle("Order Calculator");
// Specify an action for the close button.
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Create a BorderLayout manager.
setLayout(new BorderLayout());
// Create the custom panels.
banner = new GreetingPanel();
bagels = new BagelPanel();
toppings = new ToppingPanel();
coffee = new CoffeePanel();
// Create the button panel.
buildButtonPanel();
// Add the components to the content pane.
add(banner, BorderLayout.NORTH);
add(bagels, BorderLayout.WEST);
add(toppings, BorderLayout.CENTER);
add(coffee, BorderLayout.EAST);
add(buttonPanel, BorderLayout.SOUTH);
// Pack the contents of the window and display it.
pack();
setVisible(true);
}
/**
* The buildButtonPanel method builds the button panel.
*/
private void buildButtonPanel() {
// Create a panel for the buttons.
buttonPanel = new JPanel();
// Create the buttons.
calcButton = new JButton("Calculate");
exitButton = new JButton("Exit");
// Register the action listeners.
calcButton.addActionListener(new CalcButtonListener());
exitButton.addActionListener(new ExitButtonListener());
// Add the buttons to the button panel.
buttonPanel.add(calcButton);
buttonPanel.add(exitButton);
}
/**
* Private inner class that handles the event when
* the user clicks the Calculate button.
*/
private class CalcButtonListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
// Variables to hold the subtotal, tax, and total
double subtotal, tax, total;
// Calculate the subtotal.
subtotal = bagels.getBagelCost() +
toppings.getToppingCost() +
coffee.getCoffeeCost();
// Calculate the sales tax.
tax = subtotal * TAX_RATE;
// Calculate the total.
total = subtotal + tax;
// Display the charges.
JOptionPane.showMessageDialog(null,
String.format("Subtotal: $%,.2f\n" +
"Tax: $%,.2f\n" +
"Total: $%,.2f",
subtotal, tax, total));
}
}
/**
* Private inner class that handles the event when
* the user clicks the Exit button.
*/
private class ExitButtonListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
}
/**
* main method
*/
public static void main(String[] args) {
new OrderCalculatorGUI();
}
}
5. 应用程序流程
graph TD;
A[启动应用程序] --> B[显示欢迎界面和各选择面板];
B --> C{用户选择百吉饼、配料和咖啡};
C --> D[点击 Calculate 按钮];
D --> E[计算小计、销售税和总价];
E --> F[显示计算结果对话框];
C --> G[点击 Exit 按钮];
G --> H[退出应用程序];
6. 启动应用程序的操作步骤
- 确保已经安装 Java 开发环境。
-
将上述代码保存为对应的
.java文件,如GreetingPanel.java、BagelPanel.java等。 -
使用
javac命令编译所有.java文件,例如:javac OrderCalculatorGUI.java。 -
使用
java命令运行OrderCalculatorGUI类,例如:java OrderCalculatorGUI。
7. 应用程序启动时显示 Splash 屏幕
从 Java 6 开始,我们可以在 Java 应用程序启动时显示 Splash 屏幕。具体步骤如下:
1. 使用图形程序(如 Windows 下的 Microsoft Paint)创建一个 GIF、PNG 或 JPEG 格式的图像文件。
2. 在运行应用程序时,使用以下
java
命令:
java -splash:GraphicFileName ClassFileName
其中,
GraphicFileName
是包含图形图像的文件名称,
ClassFileName
是要运行的
.class
文件名称。例如,对于 Brandi’s Bagel House 应用程序,假设图像文件名为
BrandiLogo.jpg
,则使用以下命令:
java -splash:BrandiLogo.jpg OrderCalculatorGUI
8. 使用控制台输出调试 GUI 应用程序
当 GUI 应用程序出现问题时,我们可以使用
System.out.println
方法将诊断消息发送到控制台。例如,在
KiloConverter
类中,我们可以在
actionPerformed
方法中添加
System.out.println
语句来显示调试信息。
import javax.swing.*;
import java.awt.event.*;
/**
* The KiloConverter class displays a JFrame that
* lets the user enter a distance in kilometers. When
* the Calculate button is clicked, a dialog box is
* displayed with the distance converted to miles.
*/
public class KiloConverter extends JFrame {
private JPanel panel; // To reference a panel
private JLabel messageLabel; // To reference a label
private JTextField kiloTextField; // To reference a text field
private JButton calcButton; // To reference a button
private final int WINDOW_WIDTH = 310; // Window width
private final int WINDOW_HEIGHT = 100; // Window height
/**
* Constructor
*/
public KiloConverter() {
// Set the window title.
setTitle("Kilometer Converter");
// Set the size of the window.
setSize(WINDOW_WIDTH, WINDOW_HEIGHT);
// Specify what happens when the close button is clicked.
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// Build the panel and add it to the frame.
buildPanel();
// Add the panel to the frame's content pane.
add(panel);
// Display the window.
setVisible(true);
}
/**
* The buildPanel method adds a label, a text field,
* and a button to a panel.
*/
private void buildPanel() {
// Create a label to display instructions.
messageLabel = new JLabel("Enter a distance " +
"in kilometers");
// Create a text field 10 characters wide.
kiloTextField = new JTextField(10);
// Create a button with the caption "Calculate".
calcButton = new JButton("Calculate");
// Add an action listener to the button.
calcButton.addActionListener(new CalcButtonListener());
// Create a JPanel object and let the panel
// field reference it.
panel = new JPanel();
// Add the label, text field, and button
// components to the panel.
panel.add(messageLabel);
panel.add(kiloTextField);
panel.add(calcButton);
}
/**
* CalcButtonListener is an action listener class for
* the Calculate button.
*/
private class CalcButtonListener implements ActionListener {
/**
* The actionPerformed method executes when the user
* clicks on the Calculate button.
* @param e The event object.
*/
public void actionPerformed(ActionEvent e) {
final double CONVERSION = 0.6214;
String input; // To hold the user's input
double miles; // The number of miles
// Get the text entered by the user into the
// text field.
input = kiloTextField.getText();
// For debugging, display the text entered, and
// its value converted to a double.
System.out.println("Reading " + input +
" from the text field.");
System.out.println("Converted value: " +
Double.parseDouble(input));
// Convert the input to miles.
miles = Double.parseDouble(input) * CONVERSION;
// Display the result.
JOptionPane.showMessageDialog(null, input +
" kilometers is " + miles + " miles.");
// For debugging, display a message indicating
// the application is ready for more input.
System.out.println("Ready for the next input.");
}
}
/**
* The main method creates an instance of the
* KiloConverter class, which displays
* its window on the screen.
*/
public static void main(String[] args) {
new KiloConverter();
}
}
9. 调试应用程序的操作步骤
-
在需要调试的代码位置添加
System.out.println语句,如上述KiloConverter类中的示例。 - 编译并运行应用程序。
- 观察控制台输出的调试信息,根据信息排查问题。
-
当确认应用程序运行正常后,移除
System.out.println语句。
通过以上方法,我们可以更好地开发和调试 Java GUI 应用程序,提高开发效率和代码质量。
Java GUI 开发:从 JPanel 扩展类及相关技术
10. 各面板类功能总结
为了更清晰地了解各个面板类的作用和功能,我们对前面介绍的
GreetingPanel
、
BagelPanel
、
ToppingPanel
和
CoffeePanel
类进行总结,如下表所示:
| 面板类名称 | 功能描述 | 布局管理器 | 关键方法 |
| ---- | ---- | ---- | ---- |
|
GreetingPanel
| 显示欢迎信息标签 | 无(默认布局) | 无 |
|
BagelPanel
| 提供百吉饼类型选择(白百吉饼或全麦百吉饼) |
GridLayout(2, 1)
|
getBagelCost()
:返回所选百吉饼的成本 |
|
ToppingPanel
| 提供配料选择(奶油奶酪、黄油、桃果冻、蓝莓果酱) |
GridLayout(4, 1)
|
getToppingCost()
:返回所选配料的总成本 |
|
CoffeePanel
| 提供咖啡选择(无咖啡、普通咖啡、无咖啡因咖啡、卡布奇诺) |
GridLayout(4, 1)
|
getCoffeeCost()
:返回所选咖啡的成本 |
11. 事件处理机制分析
在
OrderCalculatorGUI
类中,我们使用了内部类来处理按钮的点击事件。这种方式将事件处理逻辑封装在类内部,使得代码结构更加清晰。下面我们详细分析“Calculate”和“Exit”按钮的事件处理机制。
11.1 “Calculate”按钮事件处理
private class CalcButtonListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
// Variables to hold the subtotal, tax, and total
double subtotal, tax, total;
// Calculate the subtotal.
subtotal = bagels.getBagelCost() +
toppings.getToppingCost() +
coffee.getCoffeeCost();
// Calculate the sales tax.
tax = subtotal * TAX_RATE;
// Calculate the total.
total = subtotal + tax;
// Display the charges.
JOptionPane.showMessageDialog(null,
String.format("Subtotal: $%,.2f\n" +
"Tax: $%,.2f\n" +
"Total: $%,.2f",
subtotal, tax, total));
}
}
当用户点击“Calculate”按钮时,
actionPerformed
方法会被调用。该方法首先计算订单的小计,然后根据税率计算销售税,最后计算出订单的总价。最后,使用
JOptionPane.showMessageDialog
方法显示计算结果。
11.2 “Exit”按钮事件处理
private class ExitButtonListener implements ActionListener {
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
}
当用户点击“Exit”按钮时,
actionPerformed
方法会被调用,该方法直接调用
System.exit(0)
方法退出应用程序。
12. 代码优化建议
在开发 Java GUI 应用程序时,我们可以对代码进行一些优化,以提高代码的可读性和可维护性。以下是一些优化建议:
-
提取常量
:将一些常量(如税率、商品价格等)提取到单独的类或接口中,方便统一管理和修改。
-
添加注释
:为代码添加详细的注释,特别是关键方法和复杂逻辑部分,方便其他开发者理解代码。
-
封装重复代码
:将一些重复的代码封装成方法,减少代码冗余。例如,在
OrderCalculatorGUI
类中,创建按钮和添加事件监听器的代码可以封装成一个方法。
13. 扩展功能设想
基于现有的应用程序,我们可以设想一些扩展功能,以满足更多的业务需求。以下是一些扩展功能的设想:
-
订单保存功能
:添加订单保存功能,将用户的订单信息保存到文件或数据库中,方便后续查询和统计。
-
会员系统
:引入会员系统,为会员提供折扣或积分等优惠活动。
-
多语言支持
:支持多种语言,方便不同地区的用户使用。
14. 总结
通过本文的介绍,我们学习了如何从
JPanel
类扩展类来创建自定义面板组件,以及如何使用这些组件构建复杂的 Java GUI 应用程序。我们还学习了如何处理按钮点击事件、显示 Splash 屏幕和使用控制台输出进行调试。同时,我们对代码进行了总结和优化,并设想了一些扩展功能。希望这些内容对大家在 Java GUI 开发方面有所帮助。
15. 流程图总结
下面是整个应用程序的整体流程图,展示了从启动应用程序到计算订单总价并显示结果的完整流程:
graph TD;
A[启动应用程序] --> B[显示 Splash 屏幕];
B --> C[显示欢迎界面和各选择面板];
C --> D{用户选择百吉饼、配料和咖啡};
D --> E[点击 Calculate 按钮];
E --> F[计算小计、销售税和总价];
F --> G[显示计算结果对话框];
D --> H[点击 Exit 按钮];
H --> I[退出应用程序];
通过这个流程图,我们可以更直观地了解应用程序的运行流程,有助于我们更好地理解和开发 Java GUI 应用程序。
在实际开发中,我们可以根据具体需求对应用程序进行扩展和优化,不断提升用户体验和应用程序的性能。同时,合理运用调试技巧和代码优化方法,可以提高开发效率和代码质量。希望大家在 Java GUI 开发的道路上不断探索和进步!
超级会员免费看
1925

被折叠的 条评论
为什么被折叠?



