GUI(图形用户接口)

每天学习新知识,每天进步一点点。

1. Swing概述

Swing是一种轻量级组件,它由Java语言开发,同时底层以AWT为基础,使跨平台应用程序可以使用任何可插拔的外观风格,并且Swing可以通过简洁的代码、灵活的功能和模块化组件来创建优雅的用户界面。所以同AWT相比,在实际开发中,更多的是使用Swing进行图形用户界面开发。需要注意的是,Swing并不是AWT的替代品,而是在原有的AWT的基础上进行了补充和改进。

Swing组件为实现图形用户界面提供了很多基础类库,多数位于java.awtjavax.swing包及其子包下,在这些包下提供了实现图形用户界面的主要类。其中在java.awt包及其子包下的一些类属于原有AWT组件的底层实现,而在javax.swing包及其子包下的一些类则属于Swing后期扩展的,这也从侧面反映出Swing组件对AWT组件的依赖性,接下来通过一张图来描述Swing组件的主要类,如图1所示。

Swing组件继承关系.jpg

图1 Swing组件继承关系.jpg

从图1可以看出,Swing组件的所有类都继承自Container类,然后根据GUI开发的功能扩展了2个主要分支:容器分支(包括Window窗口和Panel面板)和组件分支。其中,容器分支就是为了实现图形用户界面窗口容器而设计的,而组件分支则是为了实现向容器中填充数据、元素以及人机交互组件等功能。

在Swing组件类中,常用的顶级容器类包括有JAppletJFrameJDialog;常用的组件类中,AbstractButton类及其子类就是用来定义按钮常见行为的工具类,JTextComponent类及其子类就是用来定义文本内容编辑区域的工具类。

2. Swing顶级容器

2.1 JFrame

在Swing组件中,最常见的一个容器就是JFrame,它是一个独立存在的顶级容器(也叫窗口),不能放置在其他容器之中,JFrame支持通用窗口所有的基本功能,例如窗口最小化、设定窗口大小等。接下来通过一个案例来演示一下JFrame的使用效果,如下所示。

例2-1 Demo1.java

import javax.swing.*; // 导入Swing包中的所有类

public class Demo1 {
    public static void main(String[] args) {
        // 使用SwingUtilities确保线程安全地创建和显示窗口
        SwingUtilities.invokeLater(Demo1::createAndShow);
    }
    
    static void createAndShow() {
        // 创建一个新的JFrame对象
        JFrame frame = new JFrame("第一个窗口");
        
        // 设置关闭操作,当窗口关闭时退出程序
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        // 设置窗口的大小
        frame.setSize(600, 500);
        
        // 使窗口可见
        frame.setVisible(true);
    }
}

运行结果:
Demo1.jpg

例2-1中,通过JFrame类创建了一个窗体对象,在创建窗体对象的同时指定了窗体对象的标题为“JFrameTest”,然后通过JFrame类的相关方法分别设置了窗体对象关闭时的操作、窗体尺寸大小以及窗体信息可见。另外,在main()方法中,使用了javax.swing包下中SwingUtilities(封装有一系列操作Swing的方法集合工具类)工具类的invokeLater()方法执行了GUI程序,该方法需要传入一个接口作为参数,示例中使用到了方法引用进行传参。

2.2 JDialog

JDialog是Swing的另外一个顶级容器,通常用来表示对话框窗口。JDialog对话框可分为两种:模态对话框非模态对话框。所谓模态对话框是指用户需要等到处理完对话框后才能继续与其他窗口交互,而非模态对话框允许用户在处理对话框的同时与其他窗口交互。

对话框是模态或者非模态,可以在创建JDialog对象时为构造方法传入参数来设置,也可以在创建JDialog对象后调用它的setModal()方法来进行设置,JDialog常用的构造方法如表1所示。

表1 JDialog常用构造方法

方法声明功能描述
JDialog(Frame owner)构造方法,用来创建一个非模态的对话框,owner为对话框所有者(顶级窗口JFrame)
JDialog(Frame owner,String title)构造方法,创建一个具有指定标题的非模态对话框
JDialog(Frame owner,boolean modal)创建一个有指定模式的无标题对话框

表1中,列举了JDialog三个常用的构造方法,在这三个构造方法中都需要接收一个Frame类型的对象,表示对话框所有者。第三个构造方法中,参数modal用来指定JDialog窗口是模态还是非模态,如果modal值设置为true,对话框就是模态对话框,反之则是非模态对话框,如果不设置modal的值,其默认值为false,也就是是非模态对话框。

接下来通过一个案例来学习如何使用JDialog对话框,如下所示。
例2-2 Demo2.java

import javax.swing.*; // 导入Swing包中的所有类

public class Demo2 {
    public static void main(String[] args) {
        // 使用SwingUtilities确保线程安全地创建和显示窗口
        SwingUtilities.invokeLater(Demo2::createAndShow);
    }
    
    static void createAndShow() {
        // 创建一个新的JFrame对象,作为主窗口
        JFrame frame = new JFrame("这是一个窗口");
        
        // 设置关闭操作,当主窗口关闭时退出程序
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        // 设置主窗口的大小
        frame.setSize(600, 500);
        
        // 使主窗口可见
        frame.setVisible(true);
        
        // 创建一个新的JDialog对象,作为对话框
        JDialog dialog = new JDialog(frame, "一个对话框", true);
        
        // 设置关闭操作,当对话框关闭时隐藏对话框
        dialog.setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE);
        
        // 设置对话框的大小
        dialog.setSize(200, 200);
        
        // 使对话框可见
        dialog.setVisible(true);
    }
}

运行结果:

两个案例中,先后创建并设置了JFrame和JDialog两个容器对象,从文件代码可以看出两种顶级容器的创建方式基本相同,但从图1的结果显示看出,虽然JFrame和JDialog都可以创建顶级容器窗口,但JDialog创建的窗口右上角没有放大和缩小功能。另外,由于创建JDialog容器对象时,设置的模态参数modal为true,所以在操作时,必须先关闭JDialog对话框后才可以与JFrame窗口进行交互。

3. 布局管理器

Swing组件不能单独存在,必须放置于容器当中,而组件在容器中的位置和尺寸都是由布局管理器来决定的。Swing工具在AWT的基础上提供了8中布局管理器,分别为BorderLayout(边界布局管理器)BoxLayout(箱式布局管理器)FlowLayout(流式布局管理器)GridBagLayout(网格包Layout)GroupLayout(分组布局管理器)SpringLayout(弹性布局管理器)CardLayout(卡片布局管理器)GridLayout(网格布局管理器)。这里只介绍常用的几种布局,如果想了解更多可以自己查看手册尝试。

3.1 BorderLayout 边界布局管理器

BorderLayout(边界布局管理器)是一种较为复杂的布局方式,它将容器划分为五个区域,分别是页头(PAGE_START)、页尾(PAGE_END)、行首(LINE_START)、行尾(LINE_END)、中部(CENTER)。组件可以被放置在这五个区域中的任意一个位置。BorderLayout布局效果官方示意图如图1所示。

BorderLayout的布局
图1 BorderLayout的布局

当向BorderLayout布局管理器的容器中添加组件时,需要使用add(Component comp,Object constraints)方法,其中参数comp表示要添加的组件,constraints指定将组件添加到布局中的位置,它是一个Object类型,在传参时可以使用BorderLayout类提供的5个常量设置组件位置,它们分别是PAGE_START、PAGE_END、LINE_START、LINE_END和CENTER

接下来通过一个案例来演示一下BorderLayout布局管理器对组件布局的效果,如下所示。
例3-1 Demo3.java

import javax.swing.*; // 导入Swing包中的所有类
import java.awt.*; // 导入AWT包中的所有类

public class Demo3 {
    public static void main(String[] args) {
        // 使用SwingUtilities确保线程安全地创建和显示窗口
        SwingUtilities.invokeLater(Demo3::createAndShow);
    }
    
    static void createAndShow() {
        // 创建一个新的JFrame对象,作为主窗口
        JFrame frame = new JFrame("BorderLayout");
        
        // 设置主窗口的布局为BorderLayout
        frame.setLayout(new BorderLayout());
        
        // 设置主窗口的大小
        frame.setSize(600, 500);
        
        // 设置主窗口在屏幕上的位置
        frame.setLocation(600, 500);
        
        // 创建五个按钮
        JButton bt1 = new JButton("PAGE_START");
        JButton bt2 = new JButton("LINE_START");
        JButton bt3 = new JButton("CENTER");
        JButton bt4 = new JButton("LINE_END");
        JButton bt5 = new JButton("PAGE_END");
        
        // 将按钮添加到主窗口,并指定它们在BorderLayout中的位置
        frame.add(bt1, BorderLayout.PAGE_START); // 添加到顶部
        frame.add(bt2, BorderLayout.LINE_START); // 添加到左边
        frame.add(bt3, BorderLayout.CENTER);     // 添加到中心
        frame.add(bt4, BorderLayout.LINE_END);   // 添加到右边
        frame.add(bt5, BorderLayout.PAGE_END);   // 添加到底部
        
        // 设置关闭操作,当主窗口关闭时退出程序
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        // 使主窗口可见
        frame.setVisible(true);
    }
}

运行结果:

例3-1中,使用JFrame类的setLayout()方法为JFrame容器设置了BorderLayout布局管理器(也可以不用设置,JFrame默认就是使用BorderLayout布局管理器),同时在文件第12~16行代码中通过JButton按钮组件类的构造方法创建了5个按钮组件,并通过add()方法将这5个组件分别放入JFrame容器的5个指定区域位置。

BorderLayout的好处就是可以限定各区域的边界,当用户改变容器窗口大小时,各个组件的相对位置不变。但需要注意的是,向BorderLayout的布局管理器添加组件时,如果不指定添加到哪个区域,则默认添加到CENTER区域,并且每个区域只能放置一个组件,如果向一个区域中添加多个组件时,后放入的组件会覆盖先放入的组件

3.2 FlowLayout 流式布局管理器

FlowLayout(流式布局管理器)是最简单的布局管理器,在这种布局下,容器会将组件按照添加顺序从左向右放置,当到达容器的边界时,会自动将组件放到下一行的开始位置。这些组件可以按左对齐、居中对齐(默认方式)或右对齐的方式排列。FlowLayout类有三个构造方法,如表3-2所示。

表3-2 FlowLayout构造方法

方法声明功能描述
FlowLayout()组件默认居中对齐,水平、垂直间距默认为5个单位
FlowLayout(int align)指定组件相对于容器的对齐方式,水平、垂直间距默认为5个单位
FlowLayout(int align,int hgap,int vgap)指定组件的对齐方式和水平、垂直间距

表3-2中,列出了FlowLayout的三个构造方法,其中,参数align决定组件在每行中相对于容器边界的对齐方式,分别为左对齐、右对齐、居中对齐,可以使用该类中提供的常量FlowLayout.LEFT、FlowLayout.RIGHT、FlowLayout.CENTER表示。参数hgap和参数vgap分别设定组件之间的水平和垂直间距,可以填入一个任意数值。

接下来通过一个添加按钮的案例来学习一下FlowLayout布局管理器的用法,如下所示。
例3-2 Demo4.java

import javax.swing.*; // 导入Swing包中的所有类
import java.awt.*; // 导入AWT包中的所有类

public class Demo4 {
    public static void main(String[] args) {
        // 使用SwingUtilities确保线程安全地创建和显示窗口
        SwingUtilities.invokeLater(Demo4::createAndShow);
    }
    
    static void createAndShow() {
        // 创建一个新的JFrame对象,作为主窗口
        JFrame frame = new JFrame("FlowLayout");
        
        // 设置主窗口的布局为FlowLayout,左对齐,水平间距为20,垂直间距为30
        frame.setLayout(new FlowLayout(FlowLayout.LEFT, 20, 30));
        
        // 设置主窗口的大小
        frame.setSize(600, 500);
        
        // 设置主窗口在屏幕上的位置
        frame.setLocation(600, 500);

        // 添加五个按钮到主窗口
        frame.add(new JButton("第一个按钮")); // 第一个按钮
        frame.add(new JButton("第二个按钮")); // 第二个按钮
        frame.add(new JButton("第三个按钮")); // 第三个按钮
        frame.add(new JButton("第四个按钮")); // 第四个按钮
        frame.add(new JButton("第五个按钮")); // 第五个按钮
        
        // 设置关闭操作,当主窗口关闭时退出程序
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        // 使主窗口可见
        frame.setVisible(true);
    }
}

运行结果:

例3-2中,实现了一个流式布局管理器对按钮组件进行管理。在这个过程中首先创建了一个JFrame窗口,并将该窗口的布局管理器设置为FlowLayout,然后向窗口中添加5个按钮。通过运行图可以看出,该窗体中的按钮按照流式布局管理器的方式进行了布局。根据不同的窗口大小,组件的位置会自动变化。

3.3 GridLayout 网格布局管理器

GridLayout(网格布局管理器)使用纵横线将容器分成n行m列大小相等的网格每个网格中可以添加一个组件。添加到容器中的组件首先放置在第1行第1列(左上角)的网格中,然后在第1行的网格中从左向右依次放置其他组件,行满后,继续在下一行中从左到右放置组件。与FlowLayout不同的是,放置在GridLayout布局管理器中的组件将自动占据网格的整个区域。GridLayout类有三个构造方法,如表1所示。

表3-3 GridLayout构造方法

方法声明功能描述
GridLayout()默认只有一行,每个组件占一列
GridLayout(int rows,int cols)指定容器的行数和列数
GridLayout(int rows,int cols,int hgap,int vgap)指定容器的行数和列数以及组件之间的水平、垂直间距

表3-3中,列出了GridLayout的三个构造方法,其中,参数rows代表行数,cols代表列数,hgap和vgap规定窗格之间水平和垂直方向的间距。

接下来通过一个案例演示GridLayout布局管理器的用法,如下所示。
例3-3 Demo5.java

import javax.swing.*; // 导入Swing包中的所有类
import java.awt.*; // 导入AWT包中的所有类

public class Demo5 {
    public static void main(String[] args) {
        // 使用SwingUtilities确保线程安全地创建和显示窗口
        SwingUtilities.invokeLater(Demo5::createAndShow);
    }
    
    static void createAndShow() {
        // 创建一个新的JFrame对象,作为主窗口
        JFrame frame = new JFrame("GridLayout");
        
        // 设置主窗口的布局为GridLayout,3行3列
        frame.setLayout(new GridLayout(3, 3));
        
        // 设置主窗口的大小
        frame.setSize(600, 500);
        
        // 设置主窗口在屏幕上的位置
        frame.setLocation(600, 500);

        // 使用for循环添加8个按钮到主窗口
        for (int i = 1; i < 9; i++) {
            frame.add(new JButton("第" + i + "个按钮")); // 创建按钮并添加到窗口
        }
        
        // 设置关闭操作,当主窗口关闭时退出程序
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        // 使主窗口可见
        frame.setVisible(true);
    }
}

运行结果:

例3-3中,JFrame窗口采用GridLayout布局管理器,设置了8个按钮组件,按钮组件按照编号从左到右、从上到下填充满了整个容器。GridLayout布局管理器的特点是组件的相对位置不随区域的缩放而改变,但组件的大小会随之改变,组件始终占据网格的整个区域。缺点就是总是忽略组件的最佳大小,所有组件的宽高都相同。

4. 事件处理

4.1 事件处理机制

Swing组件中的事件处理专门用于响应用户的操作,例如,响应用户的单击鼠标、按下键盘等操作。在Swing事件处理的过程中,主要涉及到三类对象:

  • 事件源(Event Source):事件发生的场所,通常就是产生事件的组件,例如窗口、按钮、菜单等。

  • 事件对象(Event):封装了GUI组件上发生的特定事件(通常就是用户的一次操作)

  • 监听器(Listener):负责监听事件源上发生的事件,并对各种事件做出相应处理的对象(对象中包含事件处理器)。

上面提到的事件源、事件对象、监听器在整个事件处理过程中都起着非常重要的作用,它们彼此之间有着非常紧密的联系。接下来用一个图例来描述事件处理的工作流程,如图4-1所示。

事件处理流程图
图4-1 事件处理流程图

在图中,事件源是一个组件,当用户进行一些操作时,如按下鼠标或者释放键盘等,都会触发相应的事件,如果事件源注册了监听器,则触发的相应事件将会被处理。

接下来,通过一个案例来演示Swing中的事件处理,如下所示。
例4-1 Demo6.java

import java.awt.event.*; // 导入事件处理相关的类
import javax.swing.*; // 导入Swing组件类

// 自定义事件监听器类,实现ActionListener接口
class MyListener implements ActionListener {
    // 重写actionPerformed方法,当按钮被点击时会调用此方法
    public void actionPerformed(ActionEvent e) {
        System.out.println("用户点击了按钮组件"); // 输出提示信息
    }
}

public class Demo6 {
    public static void main(String[] args) {
        // 使用SwingUtilities确保线程安全地创建和显示窗口
        SwingUtilities.invokeLater(Demo6::createAndShow);
    }
	
    // 创建并显示主窗口的方法
    static void createAndShow() {
        // 创建一个新的JFrame对象,作为主窗口,并设置标题
        JFrame frame = new JFrame("窗口");
        
        // 设置主窗口的大小,宽200,高100
        frame.setSize(200, 100);
        
        // 设置主窗口在屏幕上的位置(x=600, y=500)
        frame.setLocation(600, 500);

        // 创建一个按钮,并设置按钮文本
        JButton bt = new JButton("按钮");
        // 为按钮添加动作监听器,用于处理按钮点击事件
        bt.addActionListener(new MyListener());
		
        // 将按钮添加到主窗口中
        frame.add(bt);
        
        // 设置关闭操作,当主窗口关闭时退出程序
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        // 使主窗口可见
        frame.setVisible(true);
    }
}

运行结果:

案例中,在JFrame窗口中添加了一个JButton按钮组件,同时通过addActionListener()方法为按钮组件添加了一个自定义事件监听器,当单击按钮组件时就会触发事件监听器,进行事件处理。

从上面的程序可以看出,实现Swing事件处理的主要步骤如下:

  1. 创建事件源:除了一些常见的按钮、键盘等组件可以作为事件源外,包括JFrame窗口在内的顶级容器也可以作为事件源;

  2. 自定义事件监听器:根据要监听的事件源创建指定类型的监听器进行事件处理,该监听器是一个特殊的Java类,必须实现XxxListener接口(根据组件触发的动作进行区分,如WindowListener用于监听窗口事件,ActionListener用于监听动作事件);

  3. 为事件源注册监听器:使用addXxxListener()方法为指定事件源添加特定类型的监听器。当事件源上发生监听的事件后,就会触发绑定的事件监听器,然后由监听器中的方法进行相应处理。

4.2 Swing常用事件处理

在Swing中,提供了丰富的事件,这些事件大致可以分为窗体事件(WindowEvent)、鼠标事件(MouseEvent)键盘事件(KeyEvent)动作事件(ActionEvent)等。

4.2.1 窗体事件

大部分GUI应用程序都需要使用Window窗体对象作为最外层的容器,可以说窗体对象是所有GUI应用程序的基础,应用程序中通常都是将其他组件直接或者间接地添加到窗体中。

当对窗体进行操作时,例如窗体的打开、关闭、激活、停用等,这些动作都属于窗体事件,Java中提供了一个WindowEvent类用于表示窗体事件。在应用程序中,当对窗体事件进行处理时,首先需要定义一个实现了WindowListener接口的类作为窗体监听器,然后通过addWindowListener()方法将窗体对象与窗体监听器进行绑定。

接下来通过一个案例来实现对窗体事件的监听,如下所示。
例4-2 Demo7.java

import java.awt.event.*; // 导入事件处理相关的类
import javax.swing.*; // 导入Swing组件类

public class Demo7 {
    public static void main(String[] args) {
        // 使用SwingUtilities确保线程安全地创建和显示窗口
        SwingUtilities.invokeLater(Demo7::createAndShow);
    }
	
    // 创建并显示主窗口的方法
    static void createAndShow() {
        // 创建一个新的JFrame对象,作为主窗口,并设置标题
        JFrame frame = new JFrame("窗口");
        
        // 设置主窗口的大小,宽400,高300
        frame.setSize(400, 300);
        
        // 设置主窗口在屏幕上的位置(x=600, y=500)
        frame.setLocation(600, 500);
        
        // 设置关闭操作,当主窗口关闭时退出程序
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        // 使主窗口可见
        frame.setVisible(true);
		
        // 添加窗口监听器以处理不同的窗口事件
        frame.addWindowListener(new WindowListener() {
            // 窗口打开事件
            public void windowOpened(WindowEvent e) {
                System.out.println("窗体打开事件");
            }
            
            // 窗口最小化事件
            public void windowIconified(WindowEvent e) {
                System.out.println("窗体图标化事件");
            }
            
            // 窗口恢复事件
            public void windowDeiconified(WindowEvent e) {
                System.out.println("窗体取消图标化事件");
            }
            
            // 窗口失去焦点事件
            public void windowDeactivated(WindowEvent e) {
                System.out.println("窗体停用事件");
            }
            
            // 窗口关闭前事件
            public void windowClosing(WindowEvent e) {
                System.out.println("窗体正在关闭事件");
            }
            
            // 窗口关闭事件
            public void windowClosed(WindowEvent e) {
                System.out.println("窗体关闭事件");
            }
            
            // 窗口获得焦点事件
            public void windowActivated(WindowEvent e) {
                System.out.println("窗体激活事件");
            }
        });
    }
}

运行结果:
Demo7.jpg

案例中,通过WindowListener对操作窗口的窗体事件进行监听,当接收到特定的动作后,就将所触发事件的名称打印出来。接着对窗体进行事件操作,分别执行最小化、单击任务栏图标、单击关闭按钮时,窗口事件监听器就会对相应的操作进行监听并响应,结果如图所示。

4.2.2 鼠标事件

在图形用户界面中,用户会经常使用鼠标来进行选择、切换界面等操作,这些操作被定义为鼠标事件,其中包括鼠标按下、鼠标松开、鼠标单击等。Java中提供了一个MouseEvent类用于表示鼠标事件,几乎所有的组件都可以产生鼠标事件。处理鼠标事件时,首先需要通过实现MouseListener接口定义监听器(也可以通过继承适配器MouseAdapter类来实现),然后调用addMouseListener()方法将监听器绑定到事件源对象。

接下来通过一个案例来学习如何监听鼠标事件,如下所示。
例4-3 Demo8.java

import java.awt.event.*; // 导入事件处理相关的类
import javax.swing.*; // 导入Swing组件类
import java.awt.*; // 导入AWT布局管理器类

public class Demo8 {
    public static void main(String[] args) {
        // 使用SwingUtilities确保线程安全地创建和显示窗口
        SwingUtilities.invokeLater(Demo8::createAndShow);
    }
	
    // 创建并显示主窗口的方法
    static void createAndShow() {
        // 创建一个新的JFrame对象,作为主窗口,并设置标题
        JFrame frame = new JFrame("窗口");
        
        // 设置主窗口的大小,宽400,高300
        frame.setSize(400, 300);
        
        // 设置主窗口在屏幕上的位置(x=600, y=500)
        frame.setLocation(600, 500);
        
        // 使用流式布局管理器
        frame.setLayout(new FlowLayout());
		
        // 设置关闭操作,当主窗口关闭时退出程序
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        // 使主窗口可见
        frame.setVisible(true);
		
        // 创建一个按钮,并设置按钮文本
        JButton bt = new JButton("按钮");
        
        // 为按钮添加鼠标监听器,处理不同的鼠标事件
        bt.addMouseListener(new MouseListener() {
            // 鼠标释放事件
            public void mouseReleased(MouseEvent e) {
                System.out.println("鼠标放开事件");
            }
 
            // 鼠标按下事件
            public void mousePressed(MouseEvent e) {
                System.out.println("鼠标按下事件");
            }
			
            // 鼠标移出按钮区域事件
            public void mouseExited(MouseEvent e) {
                System.out.println("鼠标移开按钮区域事件");
            }
			
            // 鼠标进入按钮区域事件
            public void mouseEntered(MouseEvent e) {
                System.out.println("鼠标进入按钮区域事件");
            }
			
            // 鼠标点击事件
            public void mouseClicked(MouseEvent e) {
                System.out.println("鼠标完成点击事件");
            }
        });
        
        // 将按钮添加到主窗口中
        frame.add(bt);
    }
}

运行结果:

Demo8.jpg

用鼠标对窗口上的按钮进行操作,先把鼠标移进按钮区域,接着单击按钮然后释放,再移出按钮区域,控制台的输出信息如图所示。
从图可以看出,当鼠标对按钮作出了相应的动作之后,监听器获取到相应的事件对象,从而打印出动作所对应的事件名称。

这个时候就会有问题,鼠标单击事件,有左键点击,右键点击,滑轮点击等,如何区分呢?对于这个问题的处理,MouseEvent类中定义了很多常量来区分鼠标动作,如下:

// 鼠标点击事件
public void mouseClicked(MouseEvent e) {
    if(e.getButton() == MouseEvent.BUTTON1){
        System.out.println("鼠标左击事件");
    }
    if(e.getButton() == MouseEvent.BUTTON2){
        System.out.println("鼠标中击事件");
    }
    if(e.getButton() == MouseEvent.BUTTON3){
        System.out.println("鼠标右击事件");
    }
}

从上面的代码可以看出,MouseEvent类中针对鼠标的按键都定义了对应的常量,可以通过MouseEvent对象的getButton()方法获取被操作按键的键值,从而判断是哪个按键的操作。另外,鼠标的单击次数也可以通过MouseEvent对象的getClickCount()方法获取到。因此,在鼠标事件中,可以根据不同的操作,做出相应的处理。

4.2.3 键盘事件

键盘操作也是最常用的用户交互方式,例如键盘按下、释放等,这些操作被定义为键盘事件。Java中提供了一个KeyEvent类表示键盘事件,处理KeyEvent事件的监听器对象需要实现KeyListener接口或者继承KeyAdapter类,然后调用addKeyListener()方法将监听器绑定到事件源对象。

接下来通过一个案例来学习如何监听键盘事件,如下所示。
例4-4 Demo9.java

import java.awt.event.*; // 导入事件处理相关的类
import javax.swing.*; // 导入Swing组件类
import java.awt.*; // 导入AWT布局管理器类

public class Demo9 {
    public static void main(String[] args) {
        // 使用SwingUtilities确保线程安全地创建和显示窗口
        SwingUtilities.invokeLater(Demo9::createAndShow);
    }
	
    // 创建并显示主窗口的方法
    static void createAndShow() {
        // 创建一个新的JFrame对象,作为主窗口,并设置标题
        JFrame frame = new JFrame("窗口");
        
        // 设置主窗口的大小,宽400,高300
        frame.setSize(400, 300);
        
        // 设置主窗口在屏幕上的位置(x=600, y=500)
        frame.setLocation(600, 500);
        
        // 使用流式布局管理器
        frame.setLayout(new FlowLayout());
		
        // 设置关闭操作,当主窗口关闭时退出程序
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        // 使主窗口可见
        frame.setVisible(true);
		
        // 创建一个文本输入框,并设置列数为30
        JTextField tf = new JTextField(30);
        
        // 为文本框添加键盘监听器,处理键盘事件
        tf.addKeyListener(new KeyAdapter() {
            // 键盘按下事件
            public void keyPressed(KeyEvent e) {
                // 获取按下的键对应的字符
                char keyChars = e.getKeyChar();
                // 获取按下的键的键码
                int keyCode = e.getKeyCode();
                // 打印按下的字符和字符代码
                System.out.println("键盘按下的字符为:" + keyChars + "\n按下的字符代码为:" + keyCode);
            }
        });
        
        // 将文本框添加到主窗口中
        frame.add(tf);
    }
}

运行结果:

Demo9.jpg

案例中,用到JTextComponent类的子类JTextField,它只允许编辑单行文本。当在文件框中键入字符时,便触发了键盘事件,会执行重写的keyPressed()方法。这时,可以通过KeyEvent类的getKeyChar()方法获取键盘键入的字符,通过getKeyCode()方法可以获取输入字符对应的整数值。在窗口中,依次输入键盘上的a、s、1字符时,控制台将按键对应的名称和键值(keyCode)打印了出来,如图所示。

4.2.4 动作事件

动作事件与前面三种事件有所不同,它不代表某类事件,只是表示一个动作发生了。例如,在关闭一个文件时,可以通过键盘关闭,也可以通过鼠标关闭。在这里读者不需要关心使用哪种方式对文件进行关闭,只要是对关闭按钮进行操作,即触发了动作事件。

在Java中,动作事件用ActionEvent类表示,处理ActionEvent事件的监听器对象需要实现ActionListener接口。监听器对象在监听动作时,不会像鼠标事件一样处理鼠标的移动和单击的细节,而是去处理类似于“按钮按下”这样“有意义”的事件。

5. Swing常用组件

5.1 面板组件

Swing组件中不仅具有JFrame和JDialog这样的顶级窗口,还提供了一些面板组件(也称之为中间容器),这些面板组件不能单独存在,只能放置在顶级窗口容器中。其中最常见的面板组件有两种:JPanelJScrollPane,接下来分别来介绍这两种面板组件。

5.1.1 JPanel

JPanel面板组件是一个无边框,不能被移动、放大、缩小或者关闭的面板,它的默认布局管理器是FlowLayout。当然也可以使用JPanel带参数的构造函数JPanel(LayoutManager layout)或者它的setLayout()方法为其制定布局管理器。

5.1.2 JScrollPane

与JPanel不同的是,JScrollPane是一个带有滚动条的面板容器,而且这个面板只能添加一个组件,如果想向JScrollPane面板中添加多个组件,应该先将这多个组件添加到某个组件中,然后再将这个组件添加到JScrollPane中。

接下来学习一下JScrollPane的常用构造方法,如表1所示。

表1 JScrollPane常用构造方法

方法声明功能描述
JScrollPane()创建一个空的JScrollPane面板
JScrollPane(Component view)创建一个显示指定组件的JScrollPane面板,只要组件的内容超过视图大小就会显示水平和垂直滚动条
JScrollPane(Component view, int vsbPolicy,int hsbPolicy)创建一个显示指定容器、并具有指定滚动条策略的JScrollPane。参数vsbPolicy和hsbPolicy分别表示垂直滚动条策略和水平滚动条策略

表1中,列出了JScrollPane常用的三个构造方法,其中,第一个构造方法用于创建一个空的JScrollPane面板;第二个构造方法用于创建显示指定组件的JScrollPane面板,这两个方法都比较简单;第三个构造方法,是在第二个构造方法的基础上指定滚动条策略。如果在构造方法中没用指定显示组件和滚动条策略,也可以使用JScrollPane提供的方法进行设置,如表2所示。

表2 JScrollPane设置面板滚动策略的方法

方法声明功能描述
void setHorizontalBarPolicy(int policy)指定水平滚动条策略,即水平滚动条何时显示在滚动面板上
void setVerticalBarPolicy(int policy)指定垂直滚动条策略,即垂直滚动条何时显示在滚动面板上
void setViewportView(Component view)设置在滚动面板显示的组件

关于上述介绍的JScrollPane面板组件的滚动策略,在ScrollPaneConstants接口中声明了多个常量属性可以用来设置不同的滚动策略,具体如表3所示。

表3 JScrollPane面板滚动策略

方法声明功能描述
VERTICAL_SCROLLBAR_AS_NEEDED HORIZONTAL_SCROLLBAR_AS_NEEDED当填充的组件视图超过客户端窗口大小时,自动显示水平和竖直放行滚动条(JscrollPane组件的默认值)
VERTICAL_SCROLLBAR_ALWAYS HORIZONTAL_SCROLLBAR_ALWAYS无论填充的组件视图大小,始终显示水平和竖直放行滚动条
VERTICAL_SCROLLBAR_NEVER HORIZONTAL_SCROLLBAR_NEVER无论填充的组件视图大小,始终不显示水平和竖直放行滚动条

接下来通过一个案例来演示面板组件的基本使用,如下所示。
例5-1 Demo10.java

import java.awt.event.*; // 导入事件处理相关的类
import javax.swing.*; // 导入Swing组件类
import java.awt.*; // 导入AWT布局管理器类

public class Demo10 {
    public static void main(String[] args) {
        // 使用SwingUtilities确保线程安全地创建和显示窗口
        SwingUtilities.invokeLater(Demo10::createAndShow);
    }
	
    // 创建并显示主窗口的方法
    static void createAndShow() {
        // 创建一个新的JFrame对象,作为主窗口,并设置标题
        JFrame frame = new JFrame("窗口");
        
        // 设置主窗口的大小,宽400,高300
        frame.setSize(400, 300);
        
        // 设置主窗口在屏幕上的位置(x=600, y=500)
        frame.setLocation(600, 500);
		
        // 设置关闭操作,当主窗口关闭时退出程序
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        // 使主窗口可见
        frame.setVisible(true);
        
        // 创建四个按钮,并设置按钮文本
        JButton bt1 = new JButton("按钮1");
        JButton bt2 = new JButton("按钮2");
        JButton bt3 = new JButton("按钮3");
        JButton bt4 = new JButton("按钮4");
		
        // 创建一个面板用于容纳按钮
        JPanel pl = new JPanel();
		
        // 将按钮添加到面板中
        pl.add(bt1);
        pl.add(bt2);
        pl.add(bt3);
        pl.add(bt4);
		
        // 创建一个滚动面板,将面板包裹起来
        JScrollPane sp = new JScrollPane(pl, 
            ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS, // 始终显示垂直滚动条
            ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED // 根据需要显示水平滚动条
        );
		
        // 将滚动面板添加到主窗口,使用中心布局
        frame.add(sp, BorderLayout.CENTER);
    }
}

运行结果:

Demo10.jpg

案例中,为了演示如何使用面板组件,分别创建了一个JScrollPane滚动面板、一个JPanel面板和四个按钮。首先将四个按钮添加到JPanel面板组件中,然后将该组件添加到JScrollPane面板中。由于JScrollPane指定的水平滚动条策略为HORIZONTAL_SCROLLBAR_AS_NEEDED,因此只有在面板区域中水平方向无法完整显示其内部放置的组件时,才会显示出水平滚动条(可以通过水平方向拉伸窗口查看效果),而JScrollPane的垂直滚动条策略为VERTICAL_SCROLLBAR_ALWAYS,所以垂直方向的滚动条会一直存在。

5.2 文本组件

文本组件用于接收用户输入的信息,其中包括文本框(JTextField)、文本域(JTextArea)等,它们都有一个共同父类JTextComponent,JTextComponent是一个抽象类,它提供了文本组件常用的方法,如表5-2所示。

表5-2 JTextComponent常用方法

方法声明功能描述
String String getText()返回文本组件中所有的文本内容
String getSelectedText()返回文本组件中选定的文本内容
void selectAll()在文本组件中选中所有内容
void setEditable()设置文本组件为可编辑或者不可编辑状态
void setText(String text)设置文本组件的内容
void replaceSelection(String content)用给定的内容替换当前选定的内容

表5-2中列出了文本组件常用的几种操作方法,其中包括选中文本内容、设置文本内容以及获取文本内容等。由于JTextField和JTextArea这两个文本组件都继承了JTextComponent类,因此它们都具有表中的方法。但在使用上还有一定的区别,接下来就对这两个文本组件进行详细讲解。

5.2.1 JTextField

JTextField称为文本框,它只能接收单行文本的输入,接下来介绍一下JTextField常用的构造方法,如表5-2-1所示。

表5-2-1 JTextField常用构造方法

方法声明功能描述
JTextField()创建一个空的文本框,初始字符串为null
JTextFiled(int columns)创建一个具有指定列数的文本框,初始字符串为null
JTextField(String text)创建一个显示指定初始字符串的文本框
JTextField(String text,int column)创建一个具有指定列数、并显示指定初始字符串的文本框

表中,列出了JTextField的四个构造方法,在创建JTextField文本框时,通常使用第二个或者第四个构造方法,指定文本框的列数。

JTextField有一个子类JPasswordField,它表示一个密码框,只能接收用户的单行输入,但是在此框中不显示用户输入的真实信息,而是通过显示指定的回显字符作为占位符。新创建的密码框默认的回显字符为“*”。JPasswordField和JTextField的构造方法相似,这里就不再介绍了。

5.2.2 JTextArea

JTextArea称为文本域,它能接收多行文本的输入,使用JTextArea构造方法创建对象时可以设定区域的行数、列数,接下来介绍一下JTextArea常用的构造方法,如表5-2-2所示。

表5-2-2 JTextArea常用构造方法

方法声明功能描述
JTextArea()构造方法,创建一个空的文本域
JTextArea(String text)构造方法,创建显示指定初始字符串的文本域
JTextArea(int rows,int columns)构造方法,创建具有指定行和列的空的文本域
JTextArea(String text,int rows,int columns)构造方法,创建显示指定初始文本并指定了行列的文本域

表5-2-2中,列出了JTextArea的四个构造方法,在创建文本域时,通常会使用最后两个构造方法,指定文本域的行数和列数。

接下来编写一个聊天窗口的案例,来演示一下文本组件JTextField和JTextArea组件的基本使用,如下所示。
例5-2 Demo11.java

import java.awt.event.*; // 导入事件处理相关的类
import javax.swing.*; // 导入Swing组件类
import java.awt.*; // 导入AWT布局管理器类

public class Demo11 {
    public static void main(String[] args) {
        // 使用SwingUtilities确保线程安全地创建和显示窗口
        SwingUtilities.invokeLater(Demo11::createAndShow);
    }
	
    // 创建并显示主窗口的方法
    static void createAndShow() {
        // 创建一个新的JFrame对象,作为主窗口,并设置标题
        JFrame frame = new JFrame("窗口");
        
        // 设置主窗口的大小,宽400,高300
        frame.setSize(400, 300);
        
        // 设置主窗口在屏幕上的位置(x=600, y=500)
        frame.setLocation(600, 500);
		
        // 设置关闭操作,当主窗口关闭时退出程序
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        // 使主窗口可见
        frame.setVisible(true);
        
        // 创建一个文本输入框,限制宽度为20列
        JTextField tl = new JTextField(20);
		
        // 创建一个文本区域,设置行数为10,列数为9
        JTextArea ta = new JTextArea(10, 9);
		
        // 创建一个发送按钮
        JButton bt1 = new JButton("发送");
		
        // 创建一个面板用于容纳文本框和按钮
        JPanel pl = new JPanel();
		
        // 将文本框和按钮添加到面板中
        pl.add(tl);
        pl.add(bt1);
		
        // 创建一个滚动面板,将文本区域包裹起来
        JScrollPane sp = new JScrollPane(ta);
		
        // 设置文本区域不可编辑
        ta.setEditable(false);
        
        // 将面板添加到窗口底部
        frame.add(pl, BorderLayout.PAGE_END);
        // 将滚动面板添加到窗口顶部
        frame.add(sp, BorderLayout.PAGE_START);
		
        // 为发送按钮添加动作监听器,用于处理按钮点击事件
        bt1.addActionListener(e -> {
            // 获取文本框的内容
            String text = tl.getText();
            // 检查文本不为空
            if (text != null && !text.trim().equals("")) {
                // 将文本框的内容在文本区域显示
                ta.append("本人输入信息:" + text + "\n");
            } else {
                // 提示用户输入不能为空
                ta.append("聊天信息不得为空\n");
            }
			
            // 清空文本框
            tl.setText("");
        });
    }
}

运行结果:

Demo11.jpg

案例中,通过JFrame模拟了一个简单的聊天窗口。首先使用JFrame顶级容器创建并设置了一个聊天窗口,同时通过BorderLayout布局管理器将窗口分为上下两个区域,并分别将一个JScrollPane滚动面板组件和一个JPanel面板组件填充到上下区域中。其中,在页头PAGE_START区域放置了一个JScrollPane滚动面板,在该面板中封装了一个JTextArea文本域用于显示聊天记录;在页尾PAGE_END区域放置了一个JPanel面板,在该面板中放置了三个组件,JLabel标签用于信息说明,JTextField文本框用于输入用户的聊天信息,JButton按钮用于发送聊天信息。

5.3 标签组件

在Swing组件中,除了有用于输入功能的文本组件外,还提供了用于仅供展示的标签组件,标签组件也是Swing中很常见的组件。Swing中的标签组件主要用到的是JLabel,JLabel组件可以显示文本、图像,还可以设置标签内容的垂直和水平对齐方式。

JLabel标签组件类包括多个构造方法,如表所示。
表5-3 Jlabel构造方法

方法声明功能描述
JLabel()创建无图像并且其标题为空字符串的JLabel
JLabel(Icon image)创建具有指定图像的JLabel实例
JLabel(Icon image, int horizontalAlignment)创建具有指定图像和水平对齐方式的JLabel实例
JLabel(String text)创建具有指定文本的JLabel实例
JLabel(String text, Icon icon, int horizontalAlignment)创建具有指定文本、图像和水平对齐方式的 JLabel 实例
JLabel(String text, int horizontalAlignment)创建具有指定文本和水平对齐方式的 JLabel 实例

表5-3中列出了JLabel标签组件的全部构造方法,在使用JLabel组件时既可以通过不同的构造方法进行初始化设置,也可以通过JLabel组件的一些常用方法进行设置。

接下来通过一个案例来演示一下JLabel标签组件的基本用法,如下所示。
例5-3 Demo12.java

import java.awt.event.*; // 导入事件处理相关的类
import javax.swing.*; // 导入Swing组件类
import java.awt.*; // 导入AWT布局管理器类

public class Demo12 {
    public static void main(String[] args) {
        // 使用SwingUtilities确保线程安全地创建和显示窗口
        SwingUtilities.invokeLater(Demo12::createAndShow);
    }
	
    // 创建并显示主窗口的方法
    static void createAndShow() {
        // 创建一个新的JFrame对象,作为主窗口,并设置标题
        JFrame frame = new JFrame("窗口");
        
        // 设置主窗口的大小,宽400,高300
        frame.setSize(400, 300);
        
        // 设置主窗口在屏幕上的位置(x=600, y=500)
        frame.setLocation(600, 500);
		
        // 设置关闭操作,当主窗口关闭时退出程序
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        // 使主窗口可见
        frame.setVisible(true);
		
        // 创建一个JLabel,用于显示图像
        JLabel lb = new JLabel();
        // 创建一个ImageIcon对象,加载指定路径的图像
        ImageIcon icon = new ImageIcon("SYY.jpg");
		
        // 获取图像,并修改其大小
        Image img = icon.getImage();
        img = img.getScaledInstance(400, 210, Image.SCALE_DEFAULT); // 将图像缩放到400x210
        icon.setImage(img); // 设置缩放后的图像给ImageIcon
        lb.setIcon(icon); // 将ImageIcon设置到JLabel上
        
        // 创建一个面板
        JPanel jp = new JPanel();
        // 创建一个新的JLabel,设置文本内容和居中对齐
        JLabel lb1 = new JLabel("大家好,我是十一月", JLabel.CENTER);
        // 将文本Label添加到面板中
        jp.add(lb1);
		
        // 将包含图像的Label添加到窗口的顶部,设置为北部
        frame.add(lb, BorderLayout.PAGE_START);
        // 将文本Label添加到窗口的底部,设置为南部
        frame.add(lb1, BorderLayout.PAGE_END);
    }
}

运行结果:

Demo12.jpg

首先使用JFrame顶级容器创建并设置了一个容器窗口,同时通过BorderLayout布局管理器将窗口分为上下两个区域,并分别加入了JLabel标签组件和JPanel面板组件。其中,在JLabel标签组件中还加入了一个ImageIcon图标组件用来显示背景图片,而在JPanel组件中封装了一个JLabel标签组件,用来显示静态文字。

5.4 按钮组件

在Swing中常见的按钮组件有JButtonJCheckBoxJRadioButton等,它们都是抽象类AbstractButton类的直接或间接子类,在AbstractButton类中提供了按钮组件通用的一些方法,如表所示。

表5-4 AbstractButton常用方法

方法声明功能描述
Icon getIcon()获取按钮的图标
void setIcon(Icon icon)设置按钮的图标
String getText()获取按钮的文本
void setText(String text)设置按钮的文本
void setEnable(boolean b)设置按钮是否可用
boolean setSelected(boolean b)设置按钮是否为选中状态
boolean isSelected()返回按钮的状态(true为选中,反之为未选中)

在前面案例中多次用到JButton按钮,而且它的使用非常简单,这里就不进行介绍了。接下来主要围绕JCheckbox和JRadioButton这两个组件进行详细讲解。

5.4.1 JCheckBox

JCheckBox组件被称为复选框组件,它有选中未选中两种状态,通常复选框会有多个,用户可以选中其中一个或者多个。表列举了JCheckBox类的常用构造方法。

表5-4-1 JcheckBox常用构造方法

方法声明功能描述
JCheckBox()创建一个没有文本信息,初始状态未被选中的复选框
JCheckBox(String text)创建一个带有文本信息,初始状态未被选定的复选框
JCheckBox(String text,boolean selected)创建一个带有文本信息,并指定初始状态(选中/未选中)的复选框

表中,列出了用于创建JCheckBox对象的三个构造方法。其中,第一个构造方法没有指定复选框的文本信息以及状态,如果想设置文本信息,可以通过调用JCheckBox从父类继承的方法来进行设置。例如调用setText(String text)来设置复选框文本信息,调用setSelected(boolean b)方法来设置复选框状态(是否被选中),也可以调用isSelected()方法来判断复选框是否被选中。第二个和第三个构造方法都指定了复选框的文本信息,而且第三个构造方法还指定了复选框初始化状态是否被选中。

接下来通过一个案例来演示一下JCheckBox复选框组件的基本用法,如下所示。
例5-4 Demo13.java

import java.awt.event.*; // 导入事件处理相关的类
import javax.swing.*; // 导入Swing组件类
import java.awt.*; // 导入AWT布局管理器类

public class Demo13 {
    public static void main(String[] args) {
        // 使用SwingUtilities确保线程安全地创建和显示窗口
        SwingUtilities.invokeLater(Demo13::createAndShow);
    }
	
    // 创建并显示主窗口的方法
    static void createAndShow() {
        // 创建一个新的JFrame对象,作为主窗口,并设置标题
        JFrame frame = new JFrame("窗口");
        
        // 设置主窗口的大小,宽400,高300
        frame.setSize(400, 300);
        
        // 设置主窗口在屏幕上的位置(x=600, y=500)
        frame.setLocation(600, 500);
		
        // 设置关闭操作,当主窗口关闭时退出程序
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        // 使主窗口可见
        frame.setVisible(true);
        
        // 创建一个JLabel,并设置文本及居中对齐
        JLabel lb = new JLabel("十一月的早晨", JLabel.CENTER);
        // 设置Label的字体类型、样式和大小
        lb.setFont(new Font("宋体", Font.PLAIN, 20));
        
        // 创建两个复选框,用于选择字体的样式
        JCheckBox cb = new JCheckBox("加粗"); // 加粗选项
        JCheckBox cb1 = new JCheckBox("斜体"); // 斜体选项
        
        // 创建动作监听器,以响应复选框的变化
        ActionListener listen = new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                // 初始字体样式设置为0
                int mode = 0;
                // 检查加粗复选框是否被选中
                if (cb.isSelected())
                    mode += Font.BOLD; // 若选中则加上加粗样式
                // 检查斜体复选框是否被选中
                if (cb1.isSelected())
                    mode += Font.ITALIC; // 若选中则加上斜体样式
                // 设置Label的字体样式
                lb.setFont(new Font("宋体", mode, 20)); // 更新Label的字体样式
            }
        };
        
        // 为复选框添加动作监听器
        cb.addActionListener(listen);
        cb1.addActionListener(listen);
        
        // 创建一个JPanel,用于容纳复选框
        JPanel jp = new JPanel();
        // 将复选框添加到面板中
        jp.add(cb);
        jp.add(cb1);
        
        // 将Label添加到主窗口中
        frame.add(lb);
        // 将面板添加到窗口底部
        frame.add(jp, BorderLayout.PAGE_END);
    }
}

Demo13.jpg
案例中,首先使用JFrame顶级容器创建并设置了一个容器窗口,同时通过BorderLayout布局管理器将窗口分为上下两个区域,并分别加入了居中的JLabel标签组件和页尾的JPanel面板组件。其中,在页尾JPanel面板组件中又添加了两个JCheckBox复选框组件,同时针对两个不同的复选框组件还添加了动作监听器。

5.4.2 JRadioButton

JRadioButton组件被称为单选按钮组件,与JCheckBox复选框不同的是,单选按钮只能选中一个,就像收音机上的电台控制按钮,当按下一个,先前按下的按钮就会自动弹起。

对于JRadioButton按钮来说,当一个按钮被选中时,先前被选中的按钮就需要自动取消选中,但是JRadioButton组件本身并不具备这种功能,因此若想实现JRadioButton按钮之间的互斥,需要使用javax.swing.ButtonGroup类。ButtonGroup是一个不可见的组件,不需要将其增加到容器中显示,只是在逻辑上表示一个单选按钮组。将多个JRadioButton按钮添加到同一个单选按钮组中就能实现JRadioButton按钮的单选功能。接下来列举一下JRadioButton的常用构造方法,如表所示。

表5-4-2 JRadioButton常用构造方法

方法声明功能描述
JRadioButton ()创建一个没有文本信息、初始状态未被选中的单选框
JRadioButton (String text)创建一个带有文本信息、初始状态未被选定的单选框
JRadioButton (String text,boolean selected)创建一个具有文本信息,并指定初始状态(选中/未选中)的单选框。

接下来通过一个案例来演示一下JRadioButton单选按钮组件的基本使用,如下所示。
例5-5 Demo14.java

import java.awt.event.*; // 导入事件处理相关的类
import javax.swing.*; // 导入Swing组件类
import java.awt.*; // 导入AWT布局管理器类

public class Demo14 {
    public static void main(String[] args) {
        // 使用SwingUtilities确保线程安全地创建和显示窗口
        SwingUtilities.invokeLater(Demo14::createAndShow);
    }
	
    // 创建并显示主窗口的方法
    static void createAndShow() {
        // 创建一个新的JFrame对象,作为主窗口,并设置标题
        JFrame frame = new JFrame("窗口");
        
        // 设置主窗口的大小,宽400,高300
        frame.setSize(400, 300);
        
        // 设置主窗口在屏幕上的位置(x=600, y=500)
        frame.setLocation(600, 500);
		
        // 设置关闭操作,当主窗口关闭时退出程序
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        // 使主窗口可见
        frame.setVisible(true);
        
        // 创建一个JLabel,并设置文本及居中对齐
        JLabel lb = new JLabel("十一月的早晨", JLabel.CENTER);
        // 设置Label的字体类型、样式和大小
        lb.setFont(new Font("宋体", Font.PLAIN, 20));
        
        // 创建一个按钮组,用于将单选按钮分组,确保同一时间只能选择一个
        ButtonGroup bg = new ButtonGroup();
        
        // 创建两个单选按钮,用于选择字体的样式
        JRadioButton cb = new JRadioButton("加粗"); // 加粗选项
        JRadioButton cb1 = new JRadioButton("斜体"); // 斜体选项
        
        // 创建动作监听器,以响应单选按钮的变化
        ActionListener listen = new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                // 初始字体样式设置为0(默认无样式)
                int mode = 0;
                // 检查加粗单选按钮是否被选中
                if (cb.isSelected())
                    mode += Font.BOLD; // 若选中则加上加粗样式
                // 检查斜体单选按钮是否被选中
                if (cb1.isSelected())
                    mode += Font.ITALIC; // 若选中则加上斜体样式
                // 设置Label的字体样式
                lb.setFont(new Font("宋体", mode, 20)); // 更新Label的字体样式
            }
        };
        
        // 将单选按钮添加到按钮组中
        bg.add(cb);
        bg.add(cb1);
        
        // 为单选按钮添加动作监听器
        cb.addActionListener(listen);
        cb1.addActionListener(listen);
        
        // 创建一个JPanel,用于容纳单选按钮
        JPanel jp = new JPanel();
        // 将单选按钮添加到面板中
        jp.add(cb);
        jp.add(cb1);
        
        // 将Label添加到主窗口中
        frame.add(lb);
        // 将面板添加到窗口底部
        frame.add(jp, BorderLayout.PAGE_END);
    }
}

Demo14.jpg

案例中,首先使用JFrame顶级容器创建并设置了一个容器窗口,同时通过BorderLayout布局管理器将窗口分为上下两个区域,并分别加入了居中的JLabel标签组件和页尾的JPanel面板组件。其中,居中区域JLabel组件放置了文本信息;页尾区域放置了一个JPanel面板,在该面板中添加了两个单选框JradioButton按钮组件,当单击这两个不同的按钮时就会触发监听器,只能选中其中一个按钮组件,然后触发并在居中区域中显示不同的效果。

5.5 下拉框组件

JComboBox组件被称为下拉框或者组合框,它将所有选项折叠在一起,默认显示的是第一个添加的选项。当用户单击下拉框时,会出现下拉式的选择列表,用户可以从中选择其中一项并显示。

JComboBox下拉框组件分为可编辑不可编辑两种形式,对于不可编辑的下拉框,用户只能选择现有的选项列表;对于可编辑的下拉框,用户既可以选择现有的选项列表,也可以自己输入新的内容。需要注意的是,自己输入的内容只能作为当前项显示,并不会添加到下拉框的选项列表中。接下来列举一下JComboBox类的常用构造方法,如表所示。

表5-5-1 JComboBox常用构造方法

方法声明功能描述
JComboBox()创建一个没有可选项的下拉框
JComboBox(Object[] items)创建一个下拉框,将Object数组中的元素作为下拉框的下拉列表选项
JComboBox(Vector items)创建一个下拉框,将Vector集合中的元素作为下拉框的下拉列表选项

在使用JComboBox下拉框组件时,需要用到它的一些常用方法,如表所示。

表5-5-2 JComboBox常用方法

方法声明功能描述
void addItem(Object anObject)为下拉框添加选项
void insertItemAt(Object anObject,int index)在指定的索引处插入选项
Objct getItemAt(int index)返回指定索引处选项,第一个选项的索引为0
int getItemCount()返回下拉框中选项的数目
Object getSelectedItem()返回当前所选项
void removeAllItems()删除下拉框中所有的选项
void removeItem(Object object)从下拉框中删除指定选项
void removeItemAt(int index)移除指定索引处的选项
void setEditable(boolean aFlag)设置下拉框的选项是否可编辑,aFlag为true则可编辑,反之则不可编辑

通过上面的两个表简单认识了JComboBox类的构造方法和常用方法,接下来通过一个案例来演示一下该组件的基本用法,如下所示。
例5-6 Demo15.java

import java.awt.event.*; // 导入事件处理相关的类
import javax.swing.*; // 导入Swing组件类
import java.awt.*; // 导入AWT布局管理器类

public class Demo15 {
    public static void main(String[] args) {
        // 使用SwingUtilities确保线程安全地创建和显示窗口
        SwingUtilities.invokeLater(Demo15::createAndShow);
    }
	
    // 创建并显示主窗口的方法
    static void createAndShow() {
        // 创建一个新的JFrame对象,作为主窗口,并设置标题
        JFrame frame = new JFrame("城市选择器"); // 设置窗口标题为"城市选择器"

        // 设置主窗口的大小,宽400,高300
        frame.setSize(400, 300);
        
        // 设置主窗口在屏幕上的位置(x=600, y=500)
        frame.setLocation(600, 500);
		
        // 设置关闭操作,当主窗口关闭时退出程序
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
        // 创建一个文本区域,用于显示选择的城市
        JTextArea ta = new JTextArea(10, 30); // 指定文本区域的行数和列数
        
        // 创建一个下拉框,添加城市选项
        JComboBox<String> cbb = new JComboBox<>();
        cbb.addItem("请选择城市"); // 默认提示项
        cbb.addItem("北京"); // 添加城市选项
        cbb.addItem("上海");
        cbb.addItem("天津");
        cbb.addItem("深圳");

        // 为下拉框添加动作监听器,用于处理城市选择事件
        cbb.addActionListener(e -> { 
            String selectedCity = (String) cbb.getSelectedItem(); // 获取选中的城市
            // 根据选择的城市更新文本区域的内容
            if (selectedCity.equals("请选择城市")) {
                ta.append("请先选择城市\n"); // 提示用户选择城市
            } else {
                ta.append("您现在选择的城市是:" + selectedCity + "\n"); // 显示选择的城市
            }
        });
        
        // 创建一个JPanel用于容纳下拉框
        JPanel jp = new JPanel();
        jp.add(cbb); // 将下拉框添加到面板中
        
        // 将面板和文本区域添加到主窗口中
        frame.add(jp, BorderLayout.PAGE_START); // 将面板放在窗口顶部
        frame.add(new JScrollPane(ta), BorderLayout.CENTER); // 在中心位置添加滚动文本区域

        // 使主窗口可见
        frame.setVisible(true);
    }
}

Demo15.jpg

首先使用JFrame顶级容器创建并设置了一个容器窗口,同时通过BorderLayout布局管理器进行设置,在容器页头加入了一个JPanel面板组件。其中,在JPanel面板组件中分别封装了一个JComboBox下拉框组件和一个JTextField文本框组件,并为JComboBox组件注册了动作监听器。

5.6 菜单组件

在GUI程序开发中,菜单是很常见的组件,利用Swing提供的菜单组件可以创建出多种样式的菜单,接下来重点对下拉式菜单和弹出式菜单进行介绍。

5.6.1 下拉式菜单

对于下拉式菜单,大家肯定很熟悉,因为计算机中很多文件的菜单都是下拉式的,如记事本的菜单。在Swing中,创建下拉式菜单需要使用三个组件:JmenuBar(菜单栏)、Jmenu(菜单)和JmenuItem(菜单项),以记事本为例,这三个组件在菜单中对应的位置如图所示。

菜单组件.jpg

在图中,分别指出了菜单的三个组件,接下来针对这三个组件进行详细讲解。

  1. JMenuBar:JMenuBar表示一个水平的菜单栏,它用来管理一组菜单,不参与同用户的交互式操作。菜单栏可以放在容器的任何位置,但通常情况下会使用顶级容器(如JFrame、Jdialog)的setJMenuBar()方法将它放置在顶级容器的顶部。JMenuBar有一个无参构造方法,创建菜单栏时,只需要使用new关键字创建JMenubar对象即可。创建完菜单栏对象后,可以调用它的add(JMenu c)方法为其添加JMenu菜单。

  2. JMenu:JMenu表示一个菜单,它用来整合管理菜单项。菜单可以是单一层次的结构,也可以是多层次的结构。大多情况下,会使用JMenu(String text)构造函数创建JMenu菜单,参数text表示菜单上的文本内容。

JMenu中还有一些常用的方法,如表5-6-1所示。

表5-6-1 JMenu常用方法

方法声明功能描述
JMenuItem add(JMenuItem menuItem)将菜单项添加到菜单末尾,返回此菜单项
void addSeparator()将分隔符添加到菜单的末尾
JMenuItem getItem(int pos)返回指定索引处的菜单项,第一个菜单项的索引为0
int getItemCount()返回菜单上的项数,菜单项和分隔符都计算在内
JMenuItem insert(JmenuItem menuItem,int pos)在指定索引处插入菜单项
void insertSeparator(int pos)在指定索引处插入分隔符
void remove(int pos)从菜单中移除指定索引处的菜单项
void remove(JMenuItem menuItem)从菜单中移除指定的菜单项
void removeAll()从菜单中移除所有的菜单项
  1. JMenuItem:JMenuItem表示一个菜单项,它是菜单系统中最基本的组件。在创建JMenuItem菜单项时,通常会使用JMenumItem(String text)这个构造方法为菜单项指定文本内容。

JMenuItem继承自AbstractButton类,因此可以把它看成是一个按钮,如果使用无参的构造方法创建了一个菜单项,则可以调用从AbstractButton类中继承的setText(String text)方法和setIcon()方法为其设置文本和图标。

介绍完创建菜单所需的三个基本组件后,接下来通过一个案例来学习一下菜单组件的基本使用,如下所示。
例5-7 Demo16.java

import java.awt.event.*; // 导入事件处理相关的类
import javax.swing.*; // 导入Swing组件类
import java.awt.*; // 导入AWT布局管理器类

public class Demo16 {
    public static void main(String[] args) {
        // 使用SwingUtilities确保线程安全地创建和显示窗口
        SwingUtilities.invokeLater(Demo16::createAndShow);
    }
	
    // 创建并显示主窗口的方法
    static void createAndShow() {
        // 创建一个新的JFrame对象,作为主窗口,并设置标题
        JFrame frame = new JFrame("菜单组件"); 

        // 设置主窗口的大小,宽400,高300
        frame.setSize(400, 300);
        
        // 设置主窗口在屏幕上的位置(x=600, y=500)
        frame.setLocation(600, 500);
		
        // 设置关闭操作,当主窗口关闭时退出程序
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      
        // 创建菜单栏
		JMenuBar menuBar = new JMenuBar();
	  
		// 创建“文件(F)”菜单
		JMenu fileMenu = new JMenu("文件(F)");
		// 创建“编辑(E)”菜单
		JMenu editMenu = new JMenu("编辑(E)");
		// 创建“搜索(S)”菜单
		JMenu searchMenu = new JMenu("搜索(S)");
	    
		// 创建菜单项
		JMenuItem newItem = new JMenuItem("新建(N)");
		JMenuItem openItem = new JMenuItem("打开(O)");
		JMenuItem undoItem = new JMenuItem("撤销(U)");
		JMenuItem findItem = new JMenuItem("查找(F)");
		
		// 将菜单项添加到相应的菜单中
		fileMenu.add(newItem);
		fileMenu.add(openItem);
		editMenu.add(undoItem);
		searchMenu.add(findItem);
		
		// 将菜单添加到菜单栏中
		menuBar.add(fileMenu);
        menuBar.add(editMenu);
		menuBar.add(searchMenu);
		
		// 将菜单栏设置到主窗口
		frame.setJMenuBar(menuBar);
		
        // 设置主窗口可见
        frame.setVisible(true);
    }
}

运行结果:
Demo16.jpg

先定义了一个JFrame容器窗口,并使用顶级容器的setJMenuBar()方法向容器顶部添加了一个下拉式菜单JMenuBar,并在JMenuBar菜单栏中添加了3个菜单JMenu组件,接着使用JMenuItem在对应的JMenu组件下添加对应的菜单项

5.6.2 弹出式菜单

对于弹出式菜单,相信大家也不会陌生,在Windows桌面单击鼠标右键会出现一个菜单,这就是弹出式菜单。在Swing组件中,弹出式菜单可以用JPopupMenu来实现。

JPopupMenu弹出式菜单和下拉式菜单一样都通过调用add()方法添加JMenuItem菜单项,但它默认是不可见的,如果想要显示出来,则必须调用它的show(Component invoker,int x,int y)方法,该方法中的参数invoker表示JPopupMenu菜单显示位置的参考组件,x和y表示invoker组件坐标空间中的一个坐标,显示的是JPopupMenu菜单的左上角坐标。

接下来通过一个案例来演示一下JPopupMenu组件的用法,如下所示。
例5-8 Demo17.java

import java.awt.event.*; // 导入事件处理相关的类
import javax.swing.*; // 导入Swing组件类
import java.awt.*; // 导入AWT布局管理器类

public class Demo17 {
    public static void main(String[] args) {
        // 使用SwingUtilities确保线程安全地创建和显示窗口
        SwingUtilities.invokeLater(Demo17::createAndShow);
    }
    
    // 创建并显示主窗口的方法
    static void createAndShow() {
        // 创建一个新的JFrame对象,作为主窗口,并设置标题
        JFrame frame = new JFrame("菜单组件"); 

        // 设置主窗口的大小,宽400,高300
        frame.setSize(400, 300);
        
        // 设置主窗口在屏幕上的位置(x=600, y=500)
        frame.setLocation(600, 500);
        
        // 设置关闭操作,当主窗口关闭时退出程序
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      
        // 创建一个弹出菜单(右键菜单)
        JPopupMenu pm = new JPopupMenu();
        
        // 创建菜单项:复制和粘贴
        JMenuItem mt = new JMenuItem("复制(C)");
        JMenuItem mt1 = new JMenuItem("粘贴(V)");
        
        // 将菜单项添加到弹出菜单中
        pm.add(mt);
        pm.add(mt1);
        
        // 为主窗口添加鼠标监听器
        frame.addMouseListener(new MouseAdapter() {
            public void mouseClicked(MouseEvent e) {
                // 检查鼠标按键是否为右键(BUTTON3)
                if (e.getButton() == MouseEvent.BUTTON3) {
                    // 显示弹出菜单,位置为鼠标点击的位置
                    pm.show(e.getComponent(), e.getX(), e.getY());
                }
            }
        });

        // 设置主窗口可见
        frame.setVisible(true);
    }
}

首先定义了一个JFrame容器窗口,接着使用JPopupMenu创建并设置了一个弹出式菜单,并为该菜单添加了2个JMenuItem菜单项。由于JPopupMenu菜单默认情况下是不显示的,所以又为JFrame窗口注册了一个鼠标事件监听器,用于显示鼠标右键单击后的弹窗列表。