1、AWT

Java 图形化界面

Java使用AWT和Swing相关的类可以完成图形化界面编程,其中AWT的全称是抽象窗口工具集(Abstract Window Toolkit),它是sun公司最早提供的GUI库,这个GUI库提供了一些基本功能,但这个GUI库的功能比较有限,所以后来sun公司又提供了Swing库。通过使用AWT和Swing提供的图形化界面组件库,java的图形化界面编程非常简单,程序只需要依次创建所需的图形组件,并以合适的方式将这些组件组织在一起,就可以开发出非常美观的用户界面。

AWT简介

当 JDK 1.0发布时, Sun 提供了 一套基本的GUI类库,这个GUI类库希望可以在所有平台下都能运行 ,这套基本类库被称为"抽象窗口工具集 CAbstract Window Toolkit )",它为Java应用程序提供了基本的图形组件 。 AWT是窗口框架,它从不同平台的窗口系统中抽取出共同组件 , 当程序运行时,将这些组件的创建和动作委托给程序所在的运行平台 。 简而言之 ,当使用 AWT 编写图形界面应用时, 程序仅指定了界面组件的位置和行为,并未提供真正的实现,JVM调用操作系统本地的图形界面来创建和平台 一致的对等体 。

使用AWT创建的图形界面应用和所在的运行平台有相同的界面风格 , 比如在 Windows 操作系统上,它就表现出 Windows 风格 ; 在 UNIX 操作系统上,它就表现出UNIX 风格 。 Sun 希望采用这种方式来实现 " Write Once, Run Anywhere " 的目标 。

AWT继承体系

所有和 AWT 编程相关的类都放在 java.awt 包以及它的子包中, AWT 编程中有两个基类:ComponentMenuComponent

image-20250430105639797

其中 Container 是一种特殊的 Component,它代表一种容器,可以装载普通的 Component

AWT中还有一个非常重要的接口叫LayoutManager ,如果一个容器中有多个组件,那么容器就需要使用LayoutManager来管理这些组件的布局方式。

image-20250430105650157

Container容器

Container继承体系

image-20250430105710994

常见API

Component作为基类,提供了如下常用的方法来设置组件的大小、位置、可见性等。

方法签名 方法功能
setLocation(int x, int y) 设置组件的位置。
setSize(int width, int height) 设置组件的大小。
setBounds(int x, int y, int width, int height) 同时设置组件的位置、大小。
setVisible(Boolean b): 设置该组件的可见性。

Container作为容器根类,提供了如下方法来访问容器中的组件。

方法签名 方法功能
Component add(Component comp) 向容器中添加其他组件 (该组件既可以是普通组件,也可以 是容器) , 并返回被添加的组件 。
Component getComponentAt(int x, int y): 返回指定点的组件 。
int getComponentCount(): 返回该容器内组件的数量 。
Component[] getComponents(): 返回该容器内的所有组件 。

Window

public class FrameDemo {
    public static void main(String[] args) {
        // 创建窗口
        Frame frame = new Frame("这是窗口名称");
        // 设置窗口大小和位置
        frame.setBounds(100,100,400,400);
        // 设置窗口可见
        frame.setVisible(true);
    }
}

image-20250430110555959

Panel

public class PanelDemo {
    public static void main(String[] args) {
        // 创建窗口
        Frame frame = new Frame("这是窗口名称");
        // 设置窗口大小和位置
        frame.setBounds(100,100,400,400);
        // 创建面板
        Panel panel = new Panel();
        // 设置面板颜色
        panel.setBackground(Color.red);
        // 添加组件
        panel.add(new Label("密码"));
        // 添加组件
        panel.add(new TextField(20));
        // 添加面板到窗口
        frame.add(panel);
        // 设置窗口可见
        frame.setVisible(true);
    }
}

image-20250430110533025

ScrollPane

public class ScrollPanelDemo {
    public static void main(String[] args) {
        Frame frame = new Frame("ScrollPanelDemo");
        frame.setBounds(100,100,200,200);
        // 创建滚动面板,并指定默认开启滚动条
        ScrollPane scrollPane = new ScrollPane(ScrollPane.SCROLLBARS_ALWAYS);
        scrollPane.add(new TextField("你好 AWT !"));
        frame.add(scrollPane);
        frame.setVisible(true);
    }
}

image-20250430111002784

LayoutManager布局管理器

之前,我们介绍了Component中有一个方法 setBounds() 可以设置当前容器的位置和大小,但是我们需要明确一件事,如果我们手动的为组件设置位置和大小的话,就会造成程序的不通用性,例如

Label label = new Label("你好,世界");

创建了一个lable组件,很多情况下,我们需要让lable组件的宽高和“你好,世界”这个字符串自身的宽高一致,这种大小称为最佳大小。由于操作系统存在差异,例如在windows上,我们要达到这样的效果,需要把该Lable组件的宽和高分别设置为100px,20px,但是在Linux操作系统上,可能需要把Lable组件的宽和高分别设置为120px,24px,才能达到同样的效果。

为了解决这个问题,Java提供了LayoutManager布局管理器,可以根据运行平台来自动调整组件大小,程序员不用再手动设置组件的大小和位置了,只需要为容器选择合适的布局管理器即可。

LayoutManager继承体系

image-20250430111243598

FlowLayout

在 FlowLayout 布局管理器中,组件像水流一样向某方向流动 (排列) ,遇到障碍(边界)就折回,重头开始排列 。在默认情况下, FlowLayout 布局管理器从左向右排列所有组件,遇到边界就会折回下一行重新开始。

构造方法 方法功能
FlowLayout() 使用默认的对齐方式及默认的垂直间距、水平间距创建 FlowLayout 布局管理器。
FlowLayout(int align) 使用指定的对齐方式及默认的垂直间距、水平间距创建 FlowLayout 布局管理器。
FlowLayout(int align,int hgap,int vgap) 使用指定的对齐方式及指定的垂直问距、水平间距创建FlowLayout 布局管理器。

align :参数应该使用FlowLayout类的静态常量 : FlowLayout. LEFTFlowLayout. CENTERFlowLayout. RIGHT ,默认是左对齐。

hgap/vgap:组件中间距通过整数设置,单位是像素,默认是5个像素。

public class FlowLayoutDemo {
    public static void main(String[] args) {
        Frame frame = new Frame("FlowLayoutDemo");
        frame.setBounds(100,100,400,200);
        // 设置流式布局,从左到右排列,并设置左右间距为2,上下间距为2
        frame.setLayout(new FlowLayout(FlowLayout.LEFT,2,2));
        for (int i = 0; i < 10; i++){
            frame.add(new Button("按钮"+i));
        }
        frame.setVisible(true);
    }
}

image-20250430111759583

BorderLayout

BorderLayout 将容器分为 EASTSOUTHWESTNORTHCENTER五个区域,普通组件可以被放置在这 5 个区域的任意一个中 。

image-20250430112007192

当改变使用 BorderLayout 的容器大小时, NORTHSOUTHCENTER区域水平调整,而 EASTWESTCENTER 区域垂直调整。使用BorderLayout 有如下两个注意点:

  1. 当向使用 BorderLayout 布局管理器的容器中添加组件时 ,需要指定要添加到哪个区域中。如果没有指定添加到哪个区域中,则默认添加到中间区域中;
  2. 如果向同一个区域中添加多个组件时,后放入的组件会覆盖先放入的组件;
  3. 如果不往某个区域中放入组件,那么该区域不会空白出来,而是会被其他区域占用;
构造方法 方法功能
BorderLayout() 使用默认的水平间距、垂直 间距创建 BorderLayout 布局管理器 。
BorderLayout(int hgap,int vgap): 使用指定的水平间距、垂直间距创建 BorderLayout 布局管理器。
public class BorderLayoutDemo {
    public static void main(String[] args) {
        Frame frame = new Frame("BorderLayoutDemo");
        frame.setBounds(100,100,300,300);
        // 设置边界布局,并设置左右间距为2,上下间距为2
        frame.setLayout(new BorderLayout(2,2));
        frame.add(new Button("NORTH"),BorderLayout.NORTH);
        frame.add(new Button("SOUTH"),BorderLayout.SOUTH);
        frame.add(new Button("EAST"),BorderLayout.EAST);
        frame.add(new Button("WEST"),BorderLayout.WEST);
        frame.add(new Button("CENTER"),BorderLayout.CENTER);
        frame.setVisible(true);
    }
}

image-20250430112353846

GridLayout

GridLayout 布局管理器将容器分割成纵横线分隔的网格 , 每个网格所占的区域大小相同。当向使用 GridLayout 布局管理器的容器中添加组件时, 默认从左向右、 从上向下依次添加到每个网格中 。

与 FlowLayout不同的是,放置在 GridLayout 布局管理器中的各组件的大小由组件所处的区域决定(每 个组件将自动占满整个区域) 。

构造方法 方法功能
GridLayout(int rows,in t cols) 采用指定的行数、列数,以及默认的横向间距、纵向间距将容器分割成多个网格
GridLayout(int rows,int cols,int hgap,int vgap) 采用指定的行数、列数 ,以及指定的横向间距 、 纵向间距将容器分割成多个网格。
public class GridLayoutDemo {

    public static void main(String[] args) {
        Frame frame = new Frame("GridLayoutDemo");
        frame.setBounds(100,100,200,300);
        Panel top = new Panel();
        top.add(new TextField(25));
        Panel bottom = new Panel();
        bottom.setLayout(new GridLayout(5,3));
        bottom.add(new Button("+"));
        bottom.add(new Button("-"));
        bottom.add(new Button("*"));
        for (int i = 0; i < 10; i++){
            bottom.add(new Button("" + i));
        }
        bottom.add(new Button("="));
        frame.add(top, BorderLayout.NORTH);
        frame.add(bottom, BorderLayout.CENTER);
        frame.setVisible(true);
    }
}

image-20250506094603629

GridBagLayout

GridBagLayout 布局管理器的功能最强大,但也最复杂,与 GridLayout 布局管理器不同的是,在GridBagLayout 布局管理器中,一个组件可以跨越一个或多个网格,并可以设置各网格的大小互不相同,从而增加了布局的灵活性。当窗口的大小发生变化时,GridBagLayout 布局管理器也可以准确地控制窗口各部分的拉伸 。

image-20250506101512470

由于在GridBagLayout 布局中,每个组件可以占用多个网格,此时,我们往容器中添加组件的时候,就需要具体的控制每个组件占用多少个网格,java提供的GridBagConstaints类,与特定的组件绑定,可以完成具体大小和跨越性的设置。

GridBagConstraints 属性如下:

属性 含义
gridx 设置受该对象控制的GUI组件左上角所在网格的横向索引
gridy 设置受该对象控制的GUI组件左上角所在网格的纵向索引
gridwidth 设置受该对象控制的 GUI 组件横向跨越多少个网格
GridBagContraints.REMAINDER:表明当前组件是横向最后一个组件
GridBagConstraints.RELATIVE:表明当前组件是横向倒数第二个组件。
gridheight 设置受该对象控制的 GUI 组件纵向跨越多少个网格
GridBagContraints.REMAINDER:表明当前组件是纵向最后一个组件
GridBagConstraints.RELATIVE:表明当前组件是纵向倒数第二个组件。
fill 当"显示区域"大于"组件"的时候,如何调整组件 :
GridBagConstraints.NONE:GUI 组件不扩大
GridBagConstraints.HORIZONTAL:GUI 组件水平扩大 以 占据空白区域
GridBagConstraints.VERTICAL:GUI 组件垂直扩大以占据空白区域
GridBagConstraints.BOTH:GUI 组件水平 、 垂直同时扩大以占据空白区域.
ipadx 设置受该对象控制的 GUI 组件横向内部填充的大小,即在该组件最小尺寸的基础上还需要增大多少.
ipady 设置受该对象控制的 GUI 组件纵向内部填充的大小,即在该组件最小尺寸的基础上还需要增大多少.
insets 设置受该对象控制 的 GUI 组件的外部填充的大小 , 即该组件边界和显示区域边界之间的距离 .
weightx 设置受该对象控制 的 GUI 组件占据多余空间的水平比例, 假设某个容器的水平线上包括三个 GUI 组件, 它们的水平增加比例分别是1、2、3,但容器宽度增加 60 像素时,则第一个组件宽度增加 10 像素 , 第二个组件宽度增加 20 像素,第三个组件宽度增加 30 像 素。 如 果其增 加比例为 0 , 则 表示不会增加 。
weighty 设置受该对象控制的 GUI 组件占据多余空间的垂直比例
anchor 设置受该对象控制的 GUI 组件在其显示区域中的定位方式:
GridBagConstraints.CENTER (中间 )
GridBagConstraints.NORTH (上中 )
GridBagConstraints.NORTHWEST (左上角)
GridBagConstraints.NORTHEAST (右上角)
GridBagConstraints.SOUTH (下中)
GridBagConstraints.SOUTHEAST (右下角)
GridBagConstraints.SOUTHWEST (左下角)
GridBagConstraints.EAST (右中)
GridBagConstraints.WEST (左中)

GridBagLayout 使用步骤如下:

public class GridBagLayoutDemo {
    public static void main(String[] args) {
        Frame frame = new Frame("GridBagLayoutDemo");
        GridBagLayout gbl = new GridBagLayout();
        frame.setLayout(gbl);

        GridBagConstraints gbc = new GridBagConstraints();
        // 设置所有的GridBagConstraints对象的fill属性为GridBagConstraints.BOTH
        // 当有空白区域时,组件自动扩大占满空白区域
        gbc.fill = GridBagConstraints.BOTH;
        // 设置GridBagConstraints对象的weightx设置为1,表示横向扩展比例为1
        gbc.weightx = 1;

        Button button1 = new Button("Button1");
        Button button2 = new Button("Button2");
        Button button3 = new Button("Button3");
        addGBLComponent(frame,button1,gbl,gbc);
        addGBLComponent(frame,button2,gbl,gbc);
        addGBLComponent(frame,button3,gbl,gbc);
        Button button4 = new Button("Button4");
        // 把GridBagConstraints的gridwidth设置为GridBagConstraints.REMAINDER
        // 则表明当前组件是横向最后一个组件
        gbc.gridwidth = GridBagConstraints.REMAINDER;
        addGBLComponent(frame,button4,gbl,gbc);

        Button button5 = new Button("Button5");
        // 由于gridwidth的值还是GridBagConstraints.REMAINDER,所以button5也会占满横向空白区域
        addGBLComponent(frame,button5,gbl,gbc);

        // 设置button6和button7的纵向向扩展比例为1
        gbc.weighty=1;
        // 把GridBagConstaints的gridheight和gridwidth设置为2,表示纵向和横向会占用两个网格
        gbc.gridwidth = 2;
        gbc.gridheight = 2;
        Button button6 = new Button("Button6");
        Button button7 = new Button("Button7");
        addGBLComponent(frame,button6,gbl,gbc);
        addGBLComponent(frame,button7,gbl,gbc);
        // 调用pack方法,让frame的大小适应组件的大小
        frame.pack();
        frame.setVisible(true);
    }

    public static void addGBLComponent(Container container,Component component,GridBagLayout gbl,GridBagConstraints gbc) {
        gbl.setConstraints(component,gbc);
        container.add(component);
    }
}

image-20250506140520857

CardLayout

CardLayout 布局管理器以时间而非空间来管理它里面的组件,它将加入容器的所有组件看成一叠卡片(每个卡片其实就是一个组件),每次只有最上面的那个 Component 才可见。就好像一副扑克牌,它们叠在一起,每次只有最上面的一张扑克牌才可见。

方法名称 方法功能
CardLayout() 创建默认的 CardLayout 布局管理器。
CardLayout(int hgap,int vgap) 通过指定卡片与容器左右边界的间距hgap 、上下边界vgap的间距来创建 CardLayout 布局管理器.
first(Container target) 显示target 容器中的第一张卡片.
last(Container target) 显示target 容器中的最后一张卡片.
previous(Container target) 显示target 容器中的前一张卡片.
next(Container target) 显示target 容器中的后一张卡片.
show(Container taget,String name) 显 示 target 容器中指定名字的卡片.
public class CardLayoutDemo {

    public static void main(String[] args) {
        Frame frame = new Frame("CardLayoutDemo");

        // 创建一个Panel容器p1,并设置其布局管理器为CardLayout,用来存放多张卡片
        Panel p1 = new Panel();
        CardLayout cardLayout = new CardLayout();
        p1.setLayout(cardLayout);
        // 添加5张卡片
        for (int i = 1; i <= 5; i++) {
            String name = "Button - " + i;
            p1.add(name,new Button(name));
        }
        
        // 创建一个Panel容器p2,用来存放上下翻页按钮
        Panel p2 = new Panel();
        // 创建按钮,并添加点击事件
        ActionListener actionListener = new ActionListener() {
            @Override
            public void actionPerformed(java.awt.event.ActionEvent e) {
                String command = e.getActionCommand();
                switch (command){
                    case "上一张":
                        cardLayout.previous(p1);
                        break;
                    case "下一张":
                        cardLayout.next(p1);
                        break;
                    case "第一张":
                        cardLayout.first(p1);
                        break;
                    case "最后一张":
                        cardLayout.last(p1);
                        break;
                    case "第三张":
                        cardLayout.show(p1,"Button - 3");
                        break;
                }
            }
        };
        Button b1 = new Button("上一张");
        Button b2 = new Button("下一张");
        Button b3 = new Button("第一张");
        Button b4 = new Button("最后一张");
        Button b5 = new Button("第三张");
        b1.addActionListener(actionListener);
        b2.addActionListener(actionListener);
        b3.addActionListener(actionListener);
        b4.addActionListener(actionListener);
        b5.addActionListener(actionListener);
        p2.add(b1);
        p2.add(b2);
        p2.add(b3);
        p2.add(b4);
        p2.add(b5);
        frame.add(p1,BorderLayout.CENTER);
        frame.add(p2,BorderLayout.SOUTH);
        frame.pack();
        frame.setVisible(true);
    }
}

image-20250506153553924

BoxLayout

为了简化开发,Swing 引入了 一个新的布局管理器 : BoxLayout 。 BoxLayout 可以在垂直和水平两个方向上摆放 GUI 组件, BoxLayout 提供了如下一个简单的构造器:

方法名称 方法功能
BoxLayout(Container target, int axis) 指定创建基于 target 容器的 BoxLayout 布局管理器,该布局管理器里的组件按 axis 方向排列。其中 axisBoxLayout.X_AXIS(横向)和 BoxLayout.Y_AXIS (纵向)两个方向。
public class BoxLayoutDemo {

    public static void main(String[] args) {
        Frame frame = new Frame("BoxLayoutDemo");
        // 创建BoxLayout布局管理器,并指定容器为上面的frame对象,指定组件排列方向为横向
        BoxLayout boxLayout = new BoxLayout(frame,BoxLayout.X_AXIS);
        frame.setLayout(boxLayout);

        frame.add(new Button("Button1"));
        frame.add(new Button("Button2"));

        frame.pack();
        frame.setVisible(true);
    }
}

image-20250506154026395

java.swing包中,提供了一个新的容器类Box,该容器的默认布局管理器就是BoxLayout,大多数情况下,使用Box容器去容纳多个GUI组件,然后再把Box容器作为一个组件,添加到其他的容器中,从而形成整体窗口布局。

Box类方法名称 方法功能
static Box createHorizontalBox() 创建一个水平排列组件的 Box 容器 。
static Box createVerticalBox() 创建一个垂直排列组件的 Box 容器 。
public class BoxLayoutDemo {

    public static void main(String[] args) {
        //创建Frame对象
        Frame frame = new Frame("BoxLayoutDemo");

        //创建一个横向的Box,并添加两个按钮
        Box hBox = Box.createHorizontalBox();
        hBox.add(new Button("水平按钮一"));
        hBox.add(new Button("水平按钮二"));

        //创建一个纵向的Box,并添加两个按钮
        Box vBox = Box.createVerticalBox();
        vBox.add(new Button("垂直按钮一"));
        vBox.add(new Button("垂直按钮二"));

        //把box容器添加到frame容器中
        frame.add(hBox,BorderLayout.NORTH);
        frame.add(vBox);

        //设置frame最佳大小并可见
        frame.pack();
        frame.setVisible(true);
    }
}

image-20250506154341970

在原有的组件需要间隔的地方,添加间隔即可,而每个间隔可以是一个组件,只不过该组件没有内容,仅仅起到一种分隔的作用。

Box类方法名称 方法功能
static Component createHorizontalGlue() 创建一条水平 Glue (可在两个方向上同时拉伸的间距)
static Component createVerticalGlue() 创建一条垂直 Glue (可在两个方向上同时拉伸的间距)
static Component createHorizontalStrut(int width) 创建一条指定宽度(宽度固定了,不能拉伸)的水平Strut (可在垂直方向上拉伸的间距)
static Component createVerticalStrut(int height) 创建一条指定高度(高度固定了,不能拉伸)的垂直Strut (可在水平方向上拉伸的间距)
public class BoxLayoutDemo {

    public static void main(String[] args) {
        //创建Frame对象
        Frame frame = new Frame("BoxLayoutDemo");

        //创建一个横向的Box,并添加两个按钮
        Box hBox = Box.createHorizontalBox();
        hBox.add(new Button("水平按钮一"));
        hBox.add(Box.createHorizontalGlue());//两个方向都可以拉伸的间隔
        hBox.add(new Button("水平按钮二"));
        hBox.add(Box.createHorizontalStrut(10));//水平间隔固定,垂直间方向可以拉伸
        hBox.add(new Button("水平按钮3"));

        //创建一个纵向的Box,并添加两个按钮
        Box vBox = Box.createVerticalBox();
        vBox.add(new Button("垂直按钮一"));
        vBox.add(Box.createVerticalGlue());//两个方向都可以拉伸的间隔
        vBox.add(new Button("垂直按钮二"));
        vBox.add(Box.createVerticalStrut(10));//垂直间隔固定,水平方向可以拉伸
        vBox.add(new Button("垂直按钮三"));

        //把box容器添加到frame容器中
        frame.add(hBox, BorderLayout.NORTH);
        frame.add(vBox);

        //设置frame最佳大小并可见
        frame.pack();
        frame.setVisible(true);
    }
}

AWT中常用组件

组件名 功能
Button 按钮
Canvas 用于绘图的画布
Checkbox 复选框组件(也可当做单选框组件使用)
CheckboxGroup 用于将多个Checkbox 组件组合成一组, 一组 Checkbox 组件将只有一个可以被选中,即全部变成单选框组件
Choice 下拉选择框
Frame 窗口 , 在 GUI 程序里通过该类创建窗口
Label 标签类,用于放置提示性文本
List JU表框组件,可以添加多项条目
Panel 不能单独存在基本容器类,必须放到其他容器中
Scrollbar 滑动条组件。如果需要用户输入位于某个范围的值 , 就可以使用滑动条组件 ,比如调 色板中设置 RGB 的三个值所用的滑动条。当创建一个滑动条时,必须指定它的方向、初始值、 滑块的大小、最小值和最大值。
ScrollPane 带水平及垂直滚动条的容器组件
TextArea 多行文本域
TextField 单行文本框
public class BaseComponentDemo {

    private Frame frame = new Frame("BaseComponentDemo");

    private TextArea textArea = new TextArea("textArea",10,30);

    private TextField textField = new TextField("textField",10);

    private CheckboxGroup cbg = new CheckboxGroup();

    private Checkbox boy = new Checkbox("男",cbg,true);

    private Checkbox girl = new Checkbox("女",cbg,false);

    private Button  button = new Button("确定");

    private Choice choice = new Choice();

    private List list = new List(11);

    private void init(){
        Panel center = new Panel();
        Panel bottom = new Panel();
        Panel right = new Panel();
        Box verticalBox = Box.createVerticalBox();
        verticalBox.add(textArea);
        verticalBox.add(textField);
        center.add(verticalBox);

        Box horizontalBox = Box.createHorizontalBox();
        horizontalBox.add(boy);
        horizontalBox.add(girl);
        choice.add("Java");
        choice.add("C++");
        choice.add("Python");
        horizontalBox.add(choice);
        horizontalBox.add(button);
        bottom.add(horizontalBox);

        list.add("Windows");
        list.add("Linux");
        list.add("MacOS");
        right.add(list);

        frame.add(center,BorderLayout.CENTER);
        frame.add(bottom,BorderLayout.SOUTH);
        frame.add(right,BorderLayout.EAST);
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        new BaseComponentDemo().init();
    }
}

image-20250506160818788

菜单组件

菜单组件名称 功能
MenuBar 菜单条,菜单的容器 。
Menu 菜单组件,菜单项的容器。它也是Menultem的子类,所以可作为菜单项使用。
PopupMenu 上下文菜单组件(右键菜单组件)
Menultem 菜单项组件 。
CheckboxMenuItem 复选框菜单项组件

image-20250506164551936

窗口菜单

public class MenuDemo {

    private Frame frame = new Frame("MenuDemo");

    private MenuBar menuBar = new MenuBar();

    private TextArea textArea = new TextArea(10,30);

    private void loadMenu() {
        Menu fileMenu = new Menu("文件");
        MenuItem openItem = new MenuItem("打开");
        MenuItem saveItem = new MenuItem("保存");
        fileMenu.add(openItem);
        fileMenu.add(saveItem);
        Menu editMenu = new Menu("编辑");
        MenuItem cutItem = new MenuItem("剪切");
        MenuItem copyItem = new MenuItem("复制");
        MenuItem pasteItem = new MenuItem("粘贴");
        editMenu.add(cutItem);
        editMenu.add(copyItem);
        editMenu.add(pasteItem);
        Menu helpMenu = new Menu("帮助");
        MenuItem aboutItem = new MenuItem("关于");
        helpMenu.add(aboutItem);
        editMenu.add(helpMenu);
        menuBar.add(fileMenu);
        menuBar.add(editMenu);
        frame.setMenuBar(menuBar);

        // 监听打开菜单
        openItem.addActionListener(e -> {
            FileDialog fd_load = new FileDialog(frame, "选择文件", FileDialog.LOAD);
            fd_load.setVisible(true);
            System.out.println("文件路径:" + fd_load.getDirectory() + fd_load.getFile());
        });
    }

    private void init(){
        loadMenu();
        frame.add(textArea);
        frame.pack();
        frame.setVisible(true);
    }

    public static void main(String[] args) {
        new MenuDemo().init();
    }
}

image-20250506165348281

右键菜单

public class PopupMenuDemo {

    private Frame frame = new Frame("PopupMenuDemo");

    //创建PopupMenu菜单
    private PopupMenu popupMenu = new PopupMenu();

    //创建菜单条
    private MenuItem commentItem = new MenuItem("注释");
    private MenuItem cancelItem = new MenuItem("取消注释");
    private MenuItem copyItem = new MenuItem("复制");
    private MenuItem pasteItem = new MenuItem("保存");
    
    //创建一个文本域
    private TextArea ta = new TextArea("Hello", 6, 40);

    //创建一个Panel
    private  Panel panel = new Panel();

    public void init(){

        //把菜单项添加到PopupMenu中
        popupMenu.add(commentItem);
        popupMenu.add(cancelItem);
        popupMenu.add(copyItem);
        popupMenu.add(pasteItem);

        //设置panel大小
        panel.setPreferredSize(new Dimension(300,100));

        //把PopupMenu添加到panel中
        panel.add(popupMenu);

        //为panel注册鼠标事件
        panel.addMouseListener(new MouseAdapter() {
            @Override
            public void mousePressed(MouseEvent e) {
                // 判断当前鼠标操作是不是触发PopupMenu的操作
                if (e.isPopupTrigger()){
                    popupMenu.show(panel,e.getX(),e.getY());
                }
            }
        });

        //把ta添加到frame中间区域中
        frame.add(ta);

        //把panel添加到frame底部
        frame.add(panel,BorderLayout.SOUTH);

        //设置frame最佳大小,并可视;
        frame.pack();
        frame.setVisible(true);

    }

    public static void main(String[] args) {
        new PopupMenuDemo().init();
    }

}

image-20250506170434857

对话框Dialog

DialogWindow 类的子类,是一个容器类,属于特殊组件。对话框是可以独立存在的顶级窗口, 因此用法与普通窗口的用法几乎完全一样,但是使用对话框需要注意下面两点:

方法名称 方法功能
Dialog(Frame owner, String title, boolean modal) 创建一个对话框对象: owner:当前对话框的父窗口 ;title:当前对话框的标题;modal:当前对话框是否是模态对话框,true/false
public class DialogDemo {

    public static void main(String[] args) {
        Frame frame = new Frame("DialogDemo");

        Dialog d1 = new Dialog(frame,"模态对话框",true);
        Dialog d2 = new Dialog(frame,"非模态对话框",false);
        d1.setBounds(100,100,300,200);
        d2.setBounds(100,100,300,200);

        Button b1 = new Button("模态对话框");
        Button b2 = new Button("非模态对话框");
        b1.addActionListener(e -> {
            d1.setVisible(true);
        });
        b2.addActionListener(e -> {
            d2.setVisible(true);
        });
        frame.add(b1,BorderLayout.NORTH);
        frame.add(b2,BorderLayout.SOUTH);
        frame.pack();
        frame.setVisible(true);
    }
}

FileDialog

Dialog 类还有 一个子类:FileDialog ,它代表一个文件对话框,用于打开或者保存文件,需要注意的是FileDialog无法指定模态或者非模态,这是因为 FileDialog 依赖于运行平台的实现,如果运行平台的文件对话框是模态的,那么 FileDialog 也是模态的;否则就是非模态的 。

方法名称 方法功能
FileDialog(Frame parent, String title, int mode) 创建一个文件对话框。parent:指定父窗口; title:对话框标题; mode:文件对话框类型,指定为FileDialog.load用于打开文件,指定为FileDialog.SAVE用于保存文件
String getDirectory() 获取被打开或保存文件的所在目录
String getFile() 获取被打开或保存文件的文件名
public class FileDialogDemo {

    public static void main(String[] args) {
        Frame frame = new Frame("FileDialogDemo");

        FileDialog fd_load = new FileDialog(frame, "选择文件", FileDialog.LOAD);
        // 只能打开 jpg、png
        fd_load.setFilenameFilter((dir, name) -> {
            return name.endsWith(".jpg") || name.endsWith(".png");
        });

        FileDialog fd_save = new FileDialog(frame, "保存文件", FileDialog.SAVE);

        Button b1 = new Button("选择文件");
        b1.addActionListener(e -> {
            fd_load.setVisible(true);
            System.out.println("文件路径:" + fd_load.getDirectory() + fd_load.getFile());
        });

        Button b2 = new Button("保存文件");
        b2.addActionListener(e -> {
            fd_save.setVisible(true);
            System.out.println("文件路径:" + fd_save.getDirectory() + fd_save.getFile());
        });
        frame.add(b1,BorderLayout.NORTH);
        frame.add(b2,BorderLayout.SOUTH);
        frame.pack();
        frame.setVisible(true);
    }
}

事件处理

前面介绍了如何放置各种组件,从而得到了丰富多彩的图形界面,但这些界面还不能响应用户的任何操作。比如单击前面所有窗口右上角的X按钮,但窗口依然不会关闭。因为在 AWT 编程中 ,所有用户的操作,都必须都需要经过一套事件处理机制来完成,而 Frame 和组件本身并没有事件处理能力 。

具体的使用步骤如下:

public class EventDemo {

    public static void main(String[] args) {
        Frame frame = new Frame("EventDemo");
        // 添加窗口关闭事件
        frame.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });

        TextField textField = new TextField(30);
        Button button = new Button("Click Me");
        // 添加点击事件
        button.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                textField.setText("Button Clicked");
            }
        });
        frame.add(textField, BorderLayout.NORTH);
        frame.add(button, BorderLayout.SOUTH);
        frame.pack();
        frame.setVisible(true);
    }
}

image-20250506163540629

常见事件和事件监听器

事件监听器必须实现事件监听器接口, AWT 提供了大量的事件监听器接口用于实现不同类型的事件监听器,用于监听不同类型的事件 。 AWT 中提供了丰富的事件类,用于封装不同组件上所发生的特定操作, AWT 的事件类都是 AWTEvent 类的子类, AWTEventEventObject 的子类。

事件

AWT把事件分为了两大类:

低级事件 触发时机
ComponentEvent 组件事件 , 当 组件尺寸发生变化、位置发生移动、显示/隐藏状态发生改变时触发该事件。
ContainerEvent 容器事件 , 当容器里发生添加组件、删除组件时触发该事件 。
WindowEvent 窗口事件, 当窗 口状态发生改变 ( 如打开、关闭、最大化、最小化)时触发该事件 。
FocusEvent 焦点事件 , 当组件得到焦点或失去焦点 时触发该事件 。
KeyEvent 键盘事件 , 当按键被按下、松开、单击时触发该事件。
MouseEvent 鼠标事件,当进行单击、按下、松开、移动鼠标等动作 时触发该事件。
PaintEvent 组件绘制事件 , 该事件是一个特殊的事件类型 , 当 GUI 组件调用 update/paint 方法来呈现自身时触发该事件,该事件并非专用于事件处理模型 。
高级事件 触发时机
ActionEvent 动作事件 ,当按钮、菜单项被单击,在 TextField 中按 Enter 键时触发
AjustmentEvent 调节事件,在滑动条上移动滑块以调节数值时触发该事件。
ltemEvent 选项事件,当用户选中某项, 或取消选中某项时触发该事件 。
TextEvent 文本事件, 当文本框、文本域里的文本发生改变时触发该事件。

事件监听器

不同的事件需要使用不同的监听器监听,不同的监听器需要实现不同的监听器接口, 当指定事件发生后 , 事件监听器就会调用所包含的事件处理器(实例方法)来处理事件 。

事件类别 描述信息 监听器接口名
ActionEvent 激活组件 ActionListener
ItemEvent 选择了某些项目 ItemListener
MouseEvent 鼠标移动 MouseMotionListener
MouseEvent 鼠标点击等 MouseListener
KeyEvent 键盘输入 KeyListener
FocusEvent 组件收到或失去焦点 FocusListener
AdjustmentEvent 移动了滚动条等组件 AdjustmentListener
ComponentEvent 对象移动缩放显示隐藏等 ComponentListener
WindowEvent 窗口收到窗口级事件 WindowListener
ContainerEvent 容器中增加删除了组件 ContainerListener
TextEvent 文本字段或文本区发生改变 TextListener