50、Java GUI 开发:从 JPanel 扩展类及相关技术

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. 启动应用程序的操作步骤
  1. 确保已经安装 Java 开发环境。
  2. 将上述代码保存为对应的 .java 文件,如 GreetingPanel.java BagelPanel.java 等。
  3. 使用 javac 命令编译所有 .java 文件,例如: javac OrderCalculatorGUI.java
  4. 使用 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. 调试应用程序的操作步骤
  1. 在需要调试的代码位置添加 System.out.println 语句,如上述 KiloConverter 类中的示例。
  2. 编译并运行应用程序。
  3. 观察控制台输出的调试信息,根据信息排查问题。
  4. 当确认应用程序运行正常后,移除 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 开发的道路上不断探索和进步!

第三方支付功能的技术人员;尤其适合从事电商、在线教育、SaaS类项目开发的工程师。; 使用场景及目标:① 实现微信与支付宝的Native、网页/APP等主流支付方式接入;② 掌握支付过程中关键的安全机制如签名验签、证书管理与敏感信息保护;③ 构建完整的支付闭环,包括下单、支付、异步通知、订单状态更新、退款与对账功能;④ 通过定时任务处理内容支付超时与概要状态不一致问题:本文详细讲解了Java,提升系统健壮性。; 阅读应用接入支付宝和建议:建议结合官方文档与沙微信支付的全流程,涵盖支付产品介绍、开发环境搭建箱环境边学边练,重点关注、安全机制、配置管理、签名核心API调用及验签逻辑、异步通知的幂等处理实际代码实现。重点与异常边界情况;包括商户号与AppID获取、API注意生产环境中的密密钥与证书配置钥安全与接口调用频率控制、使用官方SDK进行支付。下单、异步通知处理、订单查询、退款、账单下载等功能,并深入解析签名与验签、加密解密、内网穿透等关键技术环节,帮助开发者构建安全可靠的支付系统。; 适合人群:具备一定Java开发基础,熟悉Spring框架和HTTP协议,有1-3年工作经验的后端研发人员或希望快速掌握第三方支付集成的开发者。; 使用场景及目标:① 实现微信支付Native模式与支付宝PC网页支付的接入;② 掌握支付过程中核心的安全机制如签名验签、证书管理、敏感数据加密;③ 处理支付结果异步通知、订单状态核对、定时任务补偿、退款及对账等生产级功能; 阅读建议:建议结合文档中的代码示例与官方API文档同步实践,重点关注支付流程的状态一致性控制、幂等性处理和异常边界情况,建议在沙箱环境中完成全流程测试后再上线。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值