47、Java Swing编程入门指南

Java Swing编程入门指南

1. 布局管理器

在编写Swing程序之前,需要了解布局管理器。布局管理器控制容器内组件的位置。Java提供了多种布局管理器,大部分由AWT(在 java.awt 包中)提供,Swing也添加了一些自己的布局管理器。所有布局管理器都是实现了 LayoutManager 接口的类的实例(有些还实现了 LayoutManager2 接口)。以下是Swing程序员可用的一些布局管理器:
| 布局管理器 | 描述 |
| — | — |
| FlowLayout | 简单布局,组件从左到右、从上到下排列(在某些文化设置中从右到左排列)。 |
| BorderLayout | 将组件放置在容器的中心或边界。这是内容面板的默认布局。 |
| GridLayout | 在网格中布局组件。 |
| GridBagLayout | 在灵活的网格中布局不同大小的组件。 |
| BoxLayout | 在盒子中垂直或水平布局组件。 |
| SpringLayout | 根据一组约束条件布局组件。 |

1.1 BorderLayout和FlowLayout

BorderLayout 是内容面板的默认布局管理器,它定义了五个可以添加组件的位置:中心、北、南、东、西。默认情况下,向内容面板添加组件时,组件会被添加到中心位置。若要将组件添加到其他区域,需要指定其名称。

graph LR
    A(内容面板) --> B(中心)
    A --> C(北)
    A --> D(南)
    A --> E(东)
    A --> F(西)

FlowLayout 是一种更灵活的布局管理器,它一次布局一行组件,从上到下。当一行满了,就会进入下一行。虽然这种布局方式对组件的放置控制较少,但使用起来非常简单。不过,调整窗口大小时,组件的位置会改变。

2. 第一个简单的Swing程序

Swing程序与之前的基于控制台的程序不同,也与基于AWT的小程序不同。Swing程序不仅使用Swing组件集来处理用户交互,还对线程有特殊要求。理解Swing程序结构的最佳方法是通过一个示例。以下是一个简单的Swing程序:

// A simple Swing program.
import javax.swing.*;

class SwingDemo {
    SwingDemo() {
        // Create a new JFrame container.
        JFrame jfrm = new JFrame("A Simple Swing Application");
        // Give the frame an initial size.
        jfrm.setSize(275, 100);
        // Terminate the program when the user closes the application.
        jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // Create a text-based label.
        JLabel jlab = new JLabel(" Swing defines the modern Java GUI.");
        // Add the label to the content pane.
        jfrm.add(jlab);
        // Display the frame.
        jfrm.setVisible(true);
    }

    public static void main(String args[]) {
        // Create the frame on the event dispatching thread.
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new SwingDemo();
            }
        });
    }
}

2.1 程序编译和运行

编译该程序使用以下命令:

javac SwingDemo.java

运行程序使用以下命令:

java SwingDemo

2.2 代码逐行解析

  1. 导入包
import javax.swing.*;

javax.swing 包包含了Swing定义的组件和模型,所有使用Swing的程序都需要包含这个包。
2. 创建 JFrame

JFrame jfrm = new JFrame("A Simple Swing Application.");

创建一个带有标题栏、关闭、最小化、最大化和恢复按钮以及系统菜单的标准顶级窗口。
3. 设置窗口大小

jfrm.setSize(275, 100);

setSize() 方法以像素为单位设置窗口的尺寸。
4. 设置默认关闭操作

jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

关闭窗口时,整个应用程序将终止。 setDefaultCloseOperation() 方法的通用形式为:

void setDefaultCloseOperation(int what)

what 参数决定了窗口关闭时的行为,除了 JFrame.EXIT_ON_CLOSE ,还有以下选项:
| 常量 | 描述 |
| — | — |
| JFrame.DISPOSE_ON_CLOSE | 释放窗口资源 |
| JFrame.HIDE_ON_CLOSE | 隐藏窗口 |
| JFrame.DO_NOTHING_ON_CLOSE | 不做任何操作 |
5. 创建 JLabel

JLabel jlab = new JLabel(" Swing defines the modern Java GUI.");

JLabel 是最简单的Swing组件,它不接受用户输入,只是显示信息。
6. 将标签添加到内容面板

jfrm.add(jlab);

默认情况下,内容面板使用 BorderLayout 布局,此版本的 add() 方法将组件添加到中心位置。
7. 显示窗口

jfrm.setVisible(true);

setVisible() 方法的通用形式为:

void setVisible(boolean flag)

如果 flag true ,窗口将显示;否则,窗口将隐藏。默认情况下, JFrame 是不可见的,所以需要调用 setVisible(true) 来显示它。
8. 在事件调度线程上创建 SwingDemo 对象

SwingUtilities.invokeLater(new Runnable() {
    public void run() {
        new SwingDemo();
    }
});

Swing程序是事件驱动的,为了避免多线程更新组件时出现问题,所有Swing GUI组件必须在事件调度线程上创建和更新。 SwingUtilities 类提供了 invokeLater() invokeAndWait() 方法来实现这一点。

3. 使用JButton

JButton 是最常用的Swing控件之一,它继承自抽象类 AbstractButton ,定义了所有按钮的通用功能。Swing按钮可以包含文本、图像或两者都有,这里只使用基于文本的按钮。

3.1 创建JButton

JButton 提供了多个构造函数,这里使用的是:

JButton(String msg)

msg 指定了按钮内部显示的字符串。

3.2 处理按钮事件

当按下按钮时,会生成一个 ActionEvent JButton 提供了以下方法来添加或移除动作监听器:

void addActionListener(ActionListener al)
void removeActionListener(ActionListener al)

al 指定了一个接收事件通知的对象,该对象必须是实现了 ActionListener 接口的类的实例。 ActionListener 接口只定义了一个方法:

void actionPerformed(ActionEvent ae)

当按钮被按下时,会调用这个方法。可以使用传递给 actionPerformed() 方法的 ActionEvent 对象来获取与按钮按下事件相关的有用信息,例如通过调用 getActionCommand() 方法获取与按钮关联的动作命令字符串。

3.3 示例程序

// Demonstrate a push button and handle action events. 
import java.awt.*; 
import java.awt.event.*; 
import javax.swing.*; 

class ButtonDemo implements ActionListener { 
    JLabel jlab; 

    ButtonDemo() { 
        // Create a new JFrame container. 
        JFrame jfrm = new JFrame("A Button Example"); 
        // Specify FlowLayout for the layout manager. 
        jfrm.setLayout(new FlowLayout()); 
        // Give the frame an initial size. 
        jfrm.setSize(220, 90); 
        // Terminate the program when the user closes the application. 
        jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
        // Make two buttons. 
        JButton jbtnUp = new JButton("Up"); 
        JButton jbtnDown = new JButton("Down"); 
        // Add action listeners. 
        jbtnUp.addActionListener(this); 
        jbtnDown.addActionListener(this); 
        // Add the buttons to the content pane.
        jfrm.add(jbtnUp);
        jfrm.add(jbtnDown);
        // Create a label.
        jlab = new JLabel("Press a button.");
        // Add the label to the frame.
        jfrm.add(jlab);
        // Display the frame.
        jfrm.setVisible(true);
    }

    // Handle button events.
    public void actionPerformed(ActionEvent ae) {
        if(ae.getActionCommand().equals("Up"))
            jlab.setText("You pressed Up.");
        else
            jlab.setText("You pressed down. ");
    }

    public static void main(String args[]) {
        // Create the frame on the event dispatching thread.
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new ButtonDemo();
            }
        });
    }
}

3.4 程序解析

  1. 导入包
import java.awt.*; 
import java.awt.event.*; 
import javax.swing.*; 

java.awt 包包含了 FlowLayout 类, java.awt.event 包定义了 ActionListener 接口和 ActionEvent 类。
2. 实现 ActionListener 接口

class ButtonDemo implements ActionListener { 

ButtonDemo 类实现了 ActionListener 接口,意味着 ButtonDemo 对象可以接收动作事件。
3. 设置布局管理器

jfrm.setLayout(new FlowLayout());

将内容面板的布局管理器设置为 FlowLayout
4. 创建按钮并添加监听器

JButton jbtnUp = new JButton("Up"); 
JButton jbtnDown = new JButton("Down"); 
jbtnUp.addActionListener(this); 
jbtnDown.addActionListener(this);

创建两个按钮并为它们添加动作监听器。
5. 处理按钮事件

public void actionPerformed(ActionEvent ae) {
    if(ae.getActionCommand().equals("Up"))
        jlab.setText("You pressed Up.");
    else
        jlab.setText("You pressed down. ");
}

根据按钮的动作命令字符串来更新标签的文本。

4. 布局管理器的深入探讨

4.1 布局管理器的选择

在开发Swing应用程序时,选择合适的布局管理器至关重要。不同的布局管理器适用于不同的场景,以下是一些常见布局管理器的适用场景总结:
| 布局管理器 | 适用场景 |
| — | — |
| FlowLayout | 当组件的排列顺序较为简单,且不需要精确控制组件位置时,如简单的表单输入界面。 |
| BorderLayout | 适用于将界面划分为中心和四个边界区域的场景,如主窗口包含顶部菜单栏、底部状态栏和中心内容区域。 |
| GridLayout | 当需要将组件均匀分布在网格中时,如计算器的按钮布局。 |
| GridBagLayout | 对于需要灵活布局不同大小组件的复杂界面,如大型数据录入表单。 |
| BoxLayout | 用于垂直或水平排列组件,如导航栏或侧边栏。 |
| SpringLayout | 当需要根据约束条件动态调整组件位置时,如自适应窗口大小的界面。 |

4.2 布局管理器的使用示例

以下是使用 GridLayout 布局管理器的示例代码:

import javax.swing.*;
import java.awt.*;

class GridLayoutExample {
    GridLayoutExample() {
        JFrame jfrm = new JFrame("GridLayout Example");
        jfrm.setSize(300, 300);
        jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // 设置布局管理器为GridLayout,2行3列
        jfrm.setLayout(new GridLayout(2, 3));

        // 添加组件
        for (int i = 0; i < 6; i++) {
            JButton button = new JButton("Button " + (i + 1));
            jfrm.add(button);
        }

        jfrm.setVisible(true);
    }

    public static void main(String args[]) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new GridLayoutExample();
            }
        });
    }
}

在这个示例中,我们创建了一个 JFrame 并将其布局管理器设置为 GridLayout ,指定了2行3列的网格。然后添加了6个按钮,这些按钮会均匀分布在网格中。

4.3 布局管理器的嵌套使用

在实际开发中,可能需要嵌套使用不同的布局管理器来实现复杂的界面布局。例如,我们可以在一个使用 BorderLayout 的主窗口中,在中心区域使用 GridLayout 来布局子组件。以下是一个简单的嵌套布局示例:

graph LR
    A(JFrame - BorderLayout) --> B(北 - JLabel)
    A --> C(中心 - JPanel - GridLayout)
    C --> D(按钮1)
    C --> E(按钮2)
    C --> F(按钮3)
    C --> G(按钮4)
    A --> H(南 - JLabel)
import javax.swing.*;
import java.awt.*;

class NestedLayoutExample {
    NestedLayoutExample() {
        JFrame jfrm = new JFrame("Nested Layout Example");
        jfrm.setSize(400, 400);
        jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        // 设置主窗口布局为BorderLayout
        jfrm.setLayout(new BorderLayout());

        // 北区域添加标签
        JLabel northLabel = new JLabel("North Label");
        jfrm.add(northLabel, BorderLayout.NORTH);

        // 中心区域添加JPanel并设置为GridLayout
        JPanel centerPanel = new JPanel(new GridLayout(2, 2));
        for (int i = 0; i < 4; i++) {
            JButton button = new JButton("Button " + (i + 1));
            centerPanel.add(button);
        }
        jfrm.add(centerPanel, BorderLayout.CENTER);

        // 南区域添加标签
        JLabel southLabel = new JLabel("South Label");
        jfrm.add(southLabel, BorderLayout.SOUTH);

        jfrm.setVisible(true);
    }

    public static void main(String args[]) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new NestedLayoutExample();
            }
        });
    }
}

在这个示例中,我们创建了一个使用 BorderLayout 的主窗口,在北和南区域分别添加了标签,在中心区域添加了一个使用 GridLayout JPanel ,并在 JPanel 中添加了4个按钮。

5. 事件处理的扩展

5.1 多事件监听器的使用

在实际应用中,一个组件可能需要处理多种类型的事件。例如,一个按钮不仅需要处理点击事件,还可能需要处理鼠标悬停事件。以下是一个示例代码:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

class MultiEventListenerExample {
    MultiEventListenerExample() {
        JFrame jfrm = new JFrame("Multi Event Listener Example");
        jfrm.setSize(300, 200);
        jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JButton button = new JButton("Click Me");

        // 添加动作监听器
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                JOptionPane.showMessageDialog(jfrm, "Button Clicked!");
            }
        });

        // 添加鼠标监听器
        button.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseEntered(MouseEvent e) {
                button.setBackground(Color.YELLOW);
            }

            @Override
            public void mouseExited(MouseEvent e) {
                button.setBackground(null);
            }
        });

        jfrm.add(button);
        jfrm.setVisible(true);
    }

    public static void main(String args[]) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new MultiEventListenerExample();
            }
        });
    }
}

在这个示例中,我们为按钮添加了 ActionListener 来处理点击事件,当按钮被点击时会弹出一个消息框。同时,我们还添加了 MouseAdapter 来处理鼠标悬停事件,当鼠标进入按钮区域时,按钮背景颜色变为黄色,鼠标离开时恢复默认颜色。

5.2 事件处理的线程安全

在Swing程序中,事件处理必须保证线程安全。由于Swing组件不是线程安全的,所有对Swing组件的更新操作都应该在事件调度线程上执行。除了使用 SwingUtilities.invokeLater() SwingUtilities.invokeAndWait() 方法外,还可以使用 SwingWorker 类来执行耗时任务,避免阻塞事件调度线程。以下是一个使用 SwingWorker 的示例:

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

class SwingWorkerExample {
    SwingWorkerExample() {
        JFrame jfrm = new JFrame("SwingWorker Example");
        jfrm.setSize(300, 200);
        jfrm.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        JButton button = new JButton("Start Task");
        JLabel label = new JLabel("Task Not Started");

        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                // 创建并执行SwingWorker任务
                new SwingWorker<Void, Void>() {
                    @Override
                    protected Void doInBackground() throws Exception {
                        // 模拟耗时任务
                        Thread.sleep(3000);
                        return null;
                    }

                    @Override
                    protected void done() {
                        // 在事件调度线程上更新组件
                        label.setText("Task Completed");
                    }
                }.execute();
            }
        });

        jfrm.add(button, BorderLayout.NORTH);
        jfrm.add(label, BorderLayout.CENTER);
        jfrm.setVisible(true);
    }

    public static void main(String args[]) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                new SwingWorkerExample();
            }
        });
    }
}

在这个示例中,当点击按钮时,会启动一个 SwingWorker 任务,该任务在后台线程中执行耗时操作(模拟3秒的延迟)。任务完成后,会在事件调度线程上更新标签的文本。

6. 总结

通过本文的介绍,我们了解了Swing编程的基础知识,包括布局管理器的使用、简单Swing程序的创建、按钮事件处理以及事件处理的扩展。在实际开发中,我们可以根据具体需求选择合适的布局管理器和事件处理方式,同时要注意线程安全问题,确保程序的稳定性和性能。希望这些内容能帮助你快速入门Swing编程,开发出功能丰富、界面美观的Java应用程序。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值