第11章 GUI

11.1 Swing概述

Swing是Java语言开发图形化界面的一个工具包。它以抽象窗口工具包(AWT)为基础,使跨平台应用程序可以使用可插拔的外观风格。Swing拥有丰富的库和组件,使用非常灵活,开发人员只用很少的代码就可以创建出优雅的用户界面。

在Java中,所有的Swing组件都保存在javax.swing包中,为了有效的使用Swing组件,必须了解Swing包的层次结构和继承关系。下面通过一张图描述Swing组件的继承关系。

第11章 GUI_第1张图片

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

Jcomponent类几乎是所有Swing组件的公共超类,Jcomponent类的所有子类都继承了它的全部公有方法,Jcomponent的常用子类如下图。

第11章 GUI_第2张图片

在容器分支中,Swing组件类中有三个组件是继承的AWT的Window类,而不是继承自JComponent类,这三个组件是Swing中的顶级容器类,它们分别是JWindow、JFrame、和JDialog。

11.2 常用窗口

11.2.1 JFrame

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

JFrame类的常用操作方法如下表。

方法 类型 功能描述
public JFrame() throws HeadlessException 构造方法 创建一个普通窗体对象
public JFrame(String title) throws HeadlessException 构造方法 创建一个窗体对象,并指定标题
public void setSize(int width,int height) 普通方法 设置窗体大小
public void setSize(Dimention d) 普通方法 通过Dimention设置窗体大小
public void Background(Color c) 普通方法 设置窗体的背景颜色
方法 类型 功能描述
public void setLocation(int x,int y) 普通方法 设置组件的显示位置
public void setLocation(Point p) 普通方法 通过Point设置组件的显示位置
public void setVisiable(boolean b) 普通方法 显示或隐藏组件
public Component add(Component comp) 普通方法 向容器中增加组件
Public setLayout(Component comp) 普通方法 设置布局管理器,如果设置为null表示不使用
public void pack() 普通方法 调整窗口大小,以适合其子组件的首选大小和布局
public Comntainer getContentPane() 普通方法 返回此窗体的容器对象

接下来通过一个案例演示一下JFrame的使用效果。

1 import java.awt.FlowLayout;
2 import javax.swing.*;
3 class Example01 extends JFrame {
4   private static void createAndShowGUI() {
5       //创建并设置JFrame容器窗口
6       JFrame frame = new JFrame("JFrameTest");
7       //设置关闭窗口时的默认操作
8       frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
9        //设置窗口标题
10        frame.setTitle("JFrameTest");
11 //设置窗口尺寸 
12       frame.setSize(350, 300);
13        //设置窗口的显示位置
14        frame.setLocation(300,200);
15        //让组件显示
16       frame.setVisible(true);
17    }
18   public static void main(String[] args) {
19       //使用SwingUtilities工具调用createAndShowGUI()方法显示GUI程序
20        SwingUtilities.invokeLater(Example01::createAndShowGUI);
21    }
22 }       

第11章 GUI_第3张图片

上述代码中,第6行代码通过JFrame类创建了一个窗体对象frame,并在创建窗体对象的同时定义了窗体对象的标题为“JFrameTest”;第8行代码通过调用JFrame类的setDefaultCloseOperation()方法设置了窗体对象关闭时的默认操作;第10行代码通过调用JFrame类的setTitle()方法设置了窗口标题;第12行代码通过调用JFrame类的setSize()方法设置了窗口尺寸;

第14行代码通过调用JFrame类的setLocation()方法设置了窗口的显示位置;第16行代码通过调用JFrame类的setVisible()方法设置让组件显示;最后在main()方法中,调用javax.swing包中的SwingUtilities(封装有一系列操作Swing的方法集合工具类)工具类的invokeLater()方法执行了GUI程序。需要注意的是,invokeLater()方法需要传入一个接口作为参数。

11.2.2 JDialog

JDialog是Swing的另外一个顶级容器,它和Dialog一样都表示对话框窗口。JDialog对话框可分为两种,分别是模态对话框和非模态对话框。所谓模态对话框是指用户需要处理完当前对话框后才能继续与其他窗口交互的对话框,而非模态对话框是允许用户在处理对话框的同时与其他窗口交互的对话框。

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

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

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

接下来通过一个案例学习JDialog对话框的创建。

1 import java.awt.*;
2 import java.awt.event.*;
3 import javax.swing.*;
4 public class Example02 {
5	public static void main(String[] args) {
6         // 建立两个按钮
7		JButton btn1 = new JButton("模态对话框"); 
8		JButton btn2 = new JButton("非模态对话框");
9		JFrame f = new JFrame("DialogDemo");
10		f.setSize(300, 250);
11		f.setLocation(300, 200);
12		f.setLayout(new FlowLayout());   // 为内容面板设置布局管理器
13		// 在Container对象上添加按钮
14		f.add(btn1);
15		f.add(btn2);
16		// 设置单击关闭按钮默认关闭窗口
17		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
18		f.setVisible(true);
19		final JLabel label = new JLabel();
20         // 定义一个JDialog对话框
21		final JDialog dialog = new JDialog(f, "Dialog"); 		
22         dialog.setSize(220, 150);                             // 设置对话框大小
23		dialog.setLocation(350, 250);                        // 设置对话框位置
24		dialog.setLayout(new FlowLayout());                 // 设置布局管理器
25		final JButton btn3 = new JButton("确定");          // 创建按钮对象
26		dialog.add(btn3); // 在对话框的内容面板添加按钮
27		// 为"模态对话框"按钮添加单击事件
28		btn1.addActionListener(new ActionListener() {
29			public void actionPerformed(ActionEvent e) {
30				// 设置对话框为模态
31				dialog.setModal(true);
32				// 如果JDialog窗口中没有添加了JLabel标签,就把JLabel标签加上
33				if (dialog.getComponents().length == 1) {
34					dialog.add(label);
35				}
36				// 否则修改标签的内容
37				label.setText("模式对话框,点击确定按钮关闭");
38				// 显示对话框
39				dialog.setVisible(true);
40			}
41		});
42	// 为"非模态对话框"按钮添加单击事件
43	btn2.addActionListener(new ActionListener() {
44		public void actionPerformed(ActionEvent e) {
45			// 设置对话框为非模态
46			dialog.setModal(false);
47			// 如果JDialog窗口中没有添加了JLabel标签,就把JLabel标签加上
48			if (dialog.getComponents().length == 1) {
49				dialog.add(label);
50			}
51			// 否则修改标签的内容
52			label.setText("非模式对话框,点击确定按钮关闭");
53			// 显示对话框
54			dialog.setVisible(true);
55		}
56	});
57		// 为对话框中的按钮添加单击事件
58		btn3.addActionListener(new ActionListener() {
59			public void actionPerformed(ActionEvent e) {
60				dialog.dispose();
61			}
62		});
63	}
64 }

第11章 GUI_第4张图片

在模态对话框中,用户只能操作当前对话框,其他对话框都会处于一种“冰封”的状态,不能进行任何操作,直到用户单击对话框中的“确定”按钮,把该对话框关闭后,才能继续其他操作。

在模态对话框中单击【确定】按钮关闭模态对话框,然后在运行结果图中单击【非模态对话框】按钮,弹出非模态对话框,如右图。

第11章 GUI_第5张图片

11.3 布局管理器

组件在容器中的位置和尺寸是由布局管理器决定的,每当需要重新调整屏幕大小时,都要用到布局管理器。Swing常用的布局管理器有4种,分别是FlowLayout(流式布局管理器)、BorderLayout(边界布局管理器)、GridLayout(网格布局管理器)、GridBagLayout(网格包布局管理器)。Swing容器在创建时都会使用一种默认的布局管理器,在程序中可以通过调用容器对象的setLayout()方法设置布局管理器,通过布局管理器自动进行组件的布局管理。

11.3.1 FlowLayout

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

FlowLayout类的常用方法及变量如下表。

方法及常量 类型 功能描述
public FlowLayout() 构造方法 组件默认居中对齐,水平、垂直间距默认为5个单位
public FlowLayout(int align) 构造方法 指定组件相对于容器的对齐方式,水平、垂直间距默认为5个单位
public FlowLayout(int align,int hgap,int vgap) 构造方法 指定组件的对齐方式和水平、垂直间距
public static final int CENTER 常量 居中对齐
public static final int LEADING 常量 与容器的开始端对齐方式一样
public static final int LEFT 常量 左对齐
public static final int RIGHT 常量 右对齐

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

接下来通过一个案例学习FlowLayout布局管理器的用法。

1 import javax.swing.*;
2 import java.awt.*;
3 class Example03 {
4   public static void main(String[] args) {
5       JFrame frame = new JFrame("hello world");
6      //设置窗体中的布局管理器为FlowLayout,所有的组件居中对齐,水平和垂直间距为3
7       frame.setLayout(new FlowLayout(FlowLayout.CENTER,3,3));
8 JButton button = null;
9       for(int i = 0; i <9; i++){
10           button = new JButton("按钮"+i);
11            frame.add(button);
12       }
13       frame.setSize(280,250);
14       frame.setVisible(true);
15    }
16 }

第11章 GUI_第6张图片

在上述代码中,使用流式布局管理器对按钮进行管理。在这个过程中,第5行代码创建了一个JFrame窗口frame并,在创建窗体对象的同时定义了窗体对象的标题为“hello world”,第7行代码通过JFrame的setLayout属性将该窗口的布局管理器设置为FlowLayout。在第8~12行代码中,定义了一个JButton的按钮,然后使用for循环向窗口中添加9个按钮。通过运行结果可以看出,frame窗口中的按钮按照流式布局进行排列。

FlowLayout布局管理器的特点就是可以将所有组件像流水一样依次进行排列,不需要用户明确的设定,但是在灵活性上相对差了点。例如,将上图中的窗体拉伸变宽,按钮的大小和按钮之间的间距将保持不变,但按钮相对于容器边界的距离会发生变化,窗体拉伸变宽效果如右图。

第11章 GUI_第7张图片

11.3.2 BorderLayout

BorderLayout(边界布局管理器)是一种较为复杂的布局方式,它将窗体划分为五个区域,分别是东(EAST)、南(SOUTH)、西(WEST)、北(NORTH)、中(CENTER)。组件可以被放置在这五个区域中的任意一个区域中。BorderLayout的布局效果如右图。

第11章 GUI_第8张图片

BorderLayout将窗体划分为五个区域,其中箭头是指改变容器大小时,各个区域需要改变的方向。也就是说,在改变窗体大小时,NORTH和SOUTH区域高度不变,宽度调整;WEST和EAST区域宽度不变,高度调整;CENTER会相应进行调整。

当向BorderLayout管理的窗体中添加组件时,需要调用add(Component comp,Object constraints)方法,其中,参数comp表示要添加的组件,参数constraints是一个Object类型的对象,用于指定组件添加方式以及添加位置。向add()方法传参时,可以使用BorderLayout类提供的5个常量,它们分别是EAST、SOUTH、WEST、NORTH和CENTER。

BorderLayout的常用方法及常量如下表。

方法及常量 类型 功能描述
public BorderLayout() 构造方法 构造没有间距的布局器
public BorderLayout(int align,int hgap,int vgap) 构造方法 构造有水平和垂直间距的布局器
public static final String EAST 常量 将组件设置在东区域
public static final String WEST 常量 将组件设置在西区域
public static final String SOUTH 常量 将组件设置在南区域
public static final String NORTH 常量 将组件设置在北区域
public static final String CENTER 常量 将组件设置在中区域

接下来通过一个案例演示BorderLayout布局管理器对组件布局的效果。

1 import javax.swing.*;
2 import java.awt.*;
3 class BorderLayoutDemo extends JFrame {
4     //构造函数,初始化对象值
5     public BorderLayoutDemo(){
6        //设置为边界布局,组件间横向、纵向间距均为5像素
7        setLayout(new BorderLayout(5,5));
8        setFont(new Font("Helvetica", Font.PLAIN, 14));
9        //将按钮添加到窗口中
10        getContentPane().add("North", new JButton(BorderLayout.NORTH));
11 getContentPane().add("East",new JButton(BorderLayout.EAST));
12        getContentPane().add("West",new JButton(BorderLayout.WEST));
13        getContentPane().add("Center",new JButton(BorderLayout.CENTER));
14 }
15    public static void main(String args[]) {
16        BorderLayoutDemo f = new BorderLayoutDemo();
17        f.setTitle("边界布局");
18        f.pack();
19        f.setVisible(true);
20        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
21        f.setLocationRelativeTo(null);//让窗体居中显示
22    }
23 }        

第11章 GUI_第9张图片

上述代码中,第7行代码为Frame容器设置了BorderLayout布局管理器(也可以不用设置,Frame默认使用BorderLayout布局管理器),第10~14行代码在容器的东、南、西、北、中五个区域各放置了1个按钮。

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

11.3.3 GridLayout

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

GridLayout的常用构造方法如下表。

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

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

接下来通过一个案例演示GridLayout布局的用法。

1 import java.awt.*;
2 public class Example05 {
3	public static void main(String[] args) {
4		Frame f = new Frame("GridLayout");// 创建一个名为GridLayout的窗体
5		f.setLayout(new GridLayout(3, 3));// 设置该窗体为3*3的网格
6		f.setSize(300, 300);                 // 设置窗体大小
7		f.setLocation(400, 300);
8		// 下面的代码是循环添加9个按钮到GridLayout中
9		for (int i = 1; i <= 9; i++) {
10			Button btn = new Button("btn" + i);
11			f.add(btn); // 向窗体中添加按钮
12		}
13		f.setVisible(true);
14	}
15 }

第11章 GUI_第10张图片

11.3.4 GridBagLayout

GridBagLayout是最灵活、最复杂的布局管理器。GridBagLayout与GridLayout布局管理器类似,不同的是,GridBagLayout允许网格中的组件大小各不相同,而且允许一个组件跨越一个或者多个网格。

使用GridBagLayout布局管理器的步骤如下:

(1)创建GridbagLayout布局管理器,设置容器采用该布局管理器。具体示例如下:

GridBagLayout layout = new GridBagLayout();
container.setLayout(layout);

(2)创建GridBagContraints对象,并设置该对象的相关属性(设置布局约束条件)。具体示例如下:

GridBagConstraints constraints = new GridBagConstraints();
constraints.gridx = 1;     //设置网格的左上角横向索引
constraints.gridy = 1;     //设置网格的左上角纵向索引
constraints.gridwidth = 1;   //设置组件横向跨越的网格
constraints.gridheight = 1;  //设置组件纵向跨越的网格

(3)调用GridBagLayout对象的setConstraints()方法,建立GridBagConstraints对象和受控组件之间的关联。具体示例如下:

layout.setConstraints(component,constraints);

(4)向容器中添加组件。具体示例如下:

container.add(conponent);

GridBagConstraints对象可以重复使用。如果改变布局,只需要改变GridBagConstraints对象的属性即可。如果要向容器中添加多个组件,则重复(2)、(3)、(4)步骤。

从上面的步骤可以看出,使用GridBagLayout布局管理器的关键在于GridBagConstraints对象。GridBagConstraint类才是控制容器中每个组件布局的核心类,在GridBagConstraints类中有很多用于设置约束条件的属性。

GridBagConstraints类的常用属性如下表。

属性 作用
gridx和gridy 设置组件所在网格的横向和纵向索引(即所在的行和列)。如果将gridx和 gridy的值设置为GridBagConstraints.RELATIVE(默认值),表示当前组件紧跟在上一个组件后面。
gridwidth和gridheight 设置组件横向、纵向跨越几个网格,两个属性的默认值都是1。如果把这两个属性的值设为GridBagConstraints.REMAINER表示组件在当前行或列上为最后一个组件。如果把这两个属性的值设为GridBagConstraints.RELATIVE,表示组件在当前行或列上为倒数第二个组件。
属性 作用
fill 如果组件的显示区域大于组件需要的大小,设置组件改变方式,该属性接收以下几个属性值: lNONE:默认,不改变组件大小。 lHORIZONTAL:使组件水平方向足够长以填充显示区域,但是高度不变, lVERTICAL:使组件垂直方向足够高以填充显示区域,但长度不变。 lBOTH:使组件足够大,以填充整个显示区域。
weightx和weighty 设置组件占领容器中水平方向和垂直方向多余空白的比例(也称为权重)。假设容器的水平方向放置三个组件,组件的weightx属性值分别为1、2、3,当容器宽度增加60个像素时,这三个容器分别增加10、20、和30的像素。weightx和weighty属性的默认值是0,即不占领多余的空间。

如果希望组件的大小随着容器的增大而增大,必须同时设置GridBagConstraints对象的fill属性和weightx、weighty属性。

接下来通过一个案例演示GridBagLayout的用法。

1 import java.awt.*;
2 class Layout extends Frame {
3	public Layout(String title) {
4		GridBagLayout layout = new GridBagLayout();
5		GridBagConstraints c = new GridBagConstraints();
6		this.setLayout(layout);
7		c.fill = GridBagConstraints.BOTH; 	// 设置组件横向纵向可以拉伸
8		c.weightx = 1; 			// 设置横向权重为1
9		c.weighty = 1; 			// 设置纵向权重为1 
10		this.addComponent("btn1", layout, c);
11		this.addComponent("btn2", layout, c);
12		this.addComponent("btn3", layout, c);
13		c.gridwidth = GridBagConstraints.REMAINDER; 	
14         	this.addComponent("btn4", layout, c);
15		c.weightx = 0; 		// 设置横向权重为0
16		c.weighty = 0; 		// 设置纵向权重为0
17		addComponent("btn5", layout, c);
18		c.gridwidth = 1; 		// 设置组件跨一个网格(默认值)
19		this.addComponent("btn6", layout, c);
20		c.gridwidth = GridBagConstraints.REMAINDER; 	
21	     this.addComponent("btn7", layout, c);
22		c.gridheight = 2; 		// 设置组件纵向跨两个网格
23		c.gridwidth = 1;  		// 设置组件横向跨一个网格
24		c.weightx = 2;    		// 设置横向权重为2
25		c.weighty = 2;    		// 设置纵向权重为2
26		this.addComponent("btn8", layout, c);	
27		c.gridwidth = GridBagConstraints.REMAINDER;
28		c.gridheight = 1;
29		this.addComponent("btn9", layout, c);
30		this.addComponent("btn10", layout, c);
31		this.setTitle(title);
32		this.pack();
33		this.setVisible(true);
34	}
35	// 增加组件的方法
36	private void addComponent(String name, GridBagLayout layout,
37			GridBagConstraints c) {
38		Button bt = new Button(name); 	// 创建一个名为name的按钮
39		layout.setConstraints(bt, c); 
40		this.add(bt); 		// 增加按钮
41	}
42 }
43 public class Example06 {
44	public static void main(String[] args) {
45		new Layout("GridBagLayout");
46	}
47 }

第11章 GUI_第11张图片

在上述代码中,第10~30行代码向GridBagLayout管理的窗口中添加10个按钮。由于每次添加按钮的时候都需要调用该布局的setConstraints()方法,将GridBagConstraints对象与按钮组件相关联,因此,可以将这段起到关联作用的代码抽取到addComponent()方法当中,简化书写。

第10~14行代码在添加button1~button4按钮和第26~30行代码在添加button8~button10按钮时,都将权重weightx和weighty的值设置为大于0,因此在拉伸窗口时,这些按钮都会随着窗口增大。第17~21行代码在添加button5~button7按钮时,将权重值设置为0,这样它们的高度在拉伸时没有变化,但长度受上下组件的影响,还是会随窗口变大。

11.4 事件处理机制

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

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

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

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

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

第11章 GUI_第12张图片

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

接下来通过一个案例演示Swing中的事件处理。

1 import java.awt.event.*;
2 import javax.swing.*;
3 // 自定义事件监听器类
4 class MyListener implements ActionListener{
5	// 实现监听器方法,对监听事件进行处理
6	public void actionPerformed(ActionEvent e) {
7		System.out.println("用户点击了JButton按钮组件");
8	}
9 }
10 public class Example07 {
11	private static void createAndShowGUI() {
12		JFrame f = new JFrame("JFrame窗口");
13		f.setSize(200, 100); 
14		// 创建一个按钮组件,作为事件源
15		JButton btn = new JButton("按钮");  
16		// 为按钮组件事件源添加自定义监听器
17		btn.addActionListener(new MyListener());
18		f.add(btn);
19		f.setVisible(true);
20		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
21	}
22	public static void main(String[] args) {
23		// 使用SwingUtilities工具类调用createAndShowGUI()方法并显示GUI程序
24		SwingUtilities.invokeLater(Example07::createAndShowGUI);
25	}
26 }

第11章 GUI_第13张图片

上述代码中,第15行代码定义了一个btn的JButton的按钮,在第18行代码中将btn添加到JFrame窗口中,同时在第17行代码中使用addActionListener()方法为JButton按钮组件添加了一个自定义事件监听器,当单击JButton按钮组件时,自定义的事件监听器,进行事件处理。

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

(1)创建事件源:除了一些常见的按钮、键盘等组件可以作为事件源外,还可以使用JFrame窗口在内的顶级容器作为事件源。

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

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

11.4.1 Swing常用事件处理

1.窗体事件

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

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

接下来通过一个案例实现对窗体事件的监听。

1 import java.awt.event.*;
2 import javax.swing.*;
3 public class Example08 {
4	private static void createAndShowGUI() {
5		JFrame f = new JFrame("WindowEvent");
6		f.setSize(400, 300);
7		f.setLocation(300, 200);
8		f.setVisible(true);
9         f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
10		// 使用内部类创建WindowListener实例对象,监听窗体事件
11		f.addWindowListener(new WindowListener() {
12			public void windowOpened(WindowEvent e) {
13				System.out.println("windowOpened---窗体打开事件");
14			}
15	public void windowIconified(WindowEvent e) {
16		System.out.println("windowIconified---窗体图标化事件");
17	}
18	public void windowDeiconified(WindowEvent e) {
19	  System.out.println("windowDeiconified---窗体取消图标化事件");
20	}
21	public void windowDeactivated(WindowEvent e) {
22		System.out.println("windowDeactivated---窗体停用事件");
23	}
24	public void windowClosing(WindowEvent e) {
25		System.out.println("windowClosing---窗体正在关闭事件");
26	}
27	public void windowClosed(WindowEvent e) {
28		System.out.println("windowClosed---窗体关闭事件");
29	}
30				public void windowActivated(WindowEvent e) {
31				System.out.println("windowActivated---窗体激活事件");
32			}
33		});
34	}
35	public static void main(String[] args) {
36		// 使用SwingUtilities工具类调用createAndShowGUI()方法并显示GUI程序
37		SwingUtilities.invokeLater(Example08::createAndShowGUI);
38	}
39 }

第11章 GUI_第14张图片

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

第11章 GUI_第15张图片

2.鼠标事件

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

接下来通过一个案例学习如何监听鼠标事件。

1 import java.awt.*;
2 import java.awt.event.*;
3 import javax.swing.*;
4 public class Example09 {
5	private static void createAndShowGUI() {
6		JFrame f = new JFrame("MouseEvent");
7		f.setLayout(new FlowLayout());       	// 为窗口设置布局
8		f.setSize(300, 200);
9		f.setLocation(300, 200);
10		f.setVisible(true);
11         f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
12	JButton but = new JButton("Button");	// 创建按钮对象
13	f.add(but);                              	// 在窗口添加按钮组件
14	// 为按钮添加鼠标事件监听器
15	but.addMouseListener(new MouseListener() {
16		public void mouseReleased(MouseEvent e) {
17			System.out.println("mouseReleased-鼠标放开事件");
18		}
19		public void mousePressed(MouseEvent e) {
20			System.out.println("mousePressed-鼠标按下事件");
21		}
22		public void mouseExited(MouseEvent e) {
23			System.out.println("mouseExited—鼠标移出按钮区域事件");
24		}
25			public void mouseEntered(MouseEvent e) {
26				System.out.println("mouseEntered—鼠标进入按钮区域事件");
27			}
28			public void mouseClicked(MouseEvent e) {
29				System.out.println("mouseClicked-鼠标完成单击事件");
30			}
31		});
32	}
33	public static void main(String[] args) {
34		// 使用SwingUtilities工具类调用createAndShowGUI()方法并显示GUI程序
35		SwingUtilities.invokeLater(Example09::createAndShowGUI);
36	}
37 }
    

第11章 GUI_第16张图片

上述代码中,第15~31行代码通过MouseEvent对鼠标事件进行监听,当接收到特定的操作后,就将所触发事件的名称打印出来。在图11-18中,用鼠标对窗口上的按钮进行操作,先把鼠标移进按钮区域,接着单击按钮然后释放,再移出按钮区域,控制台的输出信息如下图。

第11章 GUI_第17张图片

MouseEvent类中定义了很多常量来识别鼠标操作,包括鼠标左键单/双击、滚轮操作等。如下面的代码所示:

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

3.键盘事件

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

接下来通过一个案例学习如何监听键盘事件。

1 import java.awt.*;
2 import java.awt.event.*;
3 import javax.swing.*;
4 public class Example10 {
5	private static void createAndShowGUI() {
6		JFrame f = new JFrame("KeyEvent");
7		f.setLayout(new FlowLayout());
8		f.setSize(400, 300);
9		f.setLocation(300, 200);
10		JTextField tf = new JTextField(30); // 创建文本框对象
11		f.add(tf);                               // 在窗口中添加文本框组件
12		f.setVisible(true);
13		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
14 // 为文本框添加键盘事件监听器
15		tf.addKeyListener(new KeyAdapter() {
16			public void keyPressed(KeyEvent e) {
17				// 获取对应的键盘字符
18				char keyChar = e.getKeyChar();
19				// 获取对应的键盘字符代码
20				int keyCode = e.getKeyCode();
21				System.out.print("键盘按下的字符内容为:" + keyChar+" ");
22				System.out.println("键盘按下的字符代码为:" + keyCode);
23			}
24		});
25	}
26	public static void main(String[] args) {
27		// 使用SwingUtilities工具类调用createAndShowGUI()方法并显示GUI程序
28		SwingUtilities.invokeLater(Example10::createAndShowGUI);
29	}
30  }         

第11章 GUI_第18张图片

上述代码中,第10行代码使用JTextComponent类的子类JtextField定义文本框,这里需要注意的是,JtextField类只允许编辑单行文本。在运行结果中的文本框中键入字符时,便触发了键盘事件。程序会执行代码第16~23行的keyPressed()方法。通过调用KeyEvent类的getKeyChar()方法获取键盘键入的字符,通过调用getKeyCode()方法获取输入字符对应的整数值。

在运行结果图中,依次从键盘输入a、1、2、3字符,程序会在控制台将按键对应的名称和键值(keyCode)打印出来,控制台输出结果如下图。

第11章 GUI_第19张图片

4.动作事件

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

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

11.5 Swing常用组件

前面几个小节讲解了Swing中的容器,以及开发过程中需要用到的布局管理器和事件处理机制,完成这些内容的学习后,还需要学习Swing中的组件,这样才能实现完整的GUI程序。

11.5.1 面板组件

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

1.JPanel

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

JPanel面板组件类并没有包含多少特殊的组件操作方法,大多数都是从父类(如Container)继承过来的,使用也非常简单。

2.JScrollPane

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

JScrollPane的常用构造方法如下表。

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

如果在构造方法中没有指定显示组件和滚动条策略,可以调用JScrollPane提供的成员方法进行设置,JScrollPane设置面板滚动策略的方法如下表。

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

关于上述介绍的JScrollPane面板组件滚动策略的设置方法,ScrollPaneConstants接口声明了多个常量属性,可以用来设置不同的滚动策略。JscrollPane的滚动属性如下表。

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

接下来通过一个案例演示面板组件的基本用法。

import java.awt.*;
import javax.swing.*;
public class Example11 {
	private static void createAndShowGUI() {
		// 1、创建一个JFrame容器窗口
		JFrame f = new JFrame("PanelDemo");
		f.setLayout(new BorderLayout());
		f.setSize(350, 200);
		f.setLocation(300, 200);
		f.setVisible(true);
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		// 2、创建JScrollPane滚动面板组件
		JScrollPane scrollPane = new JScrollPane();
		// 设置水平滚动条策略--滚动条需要时显示
		scrollPane.setHorizontalScrollBarPolicy
				(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED);
		// 设置垂直滚动条策略--滚动条一直显示
		scrollPane.setVerticalScrollBarPolicy
				(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
		// 定义一个JPanel面板组件
		JPanel panel = new JPanel();
		// 在JPanel面板中添加四个按钮
		panel.add(new JButton("按钮1"));
		panel.add(new JButton("按钮2"));
		panel.add(new JButton("按钮3"));
		panel.add(new JButton("按钮4"));
		// 设置JPanel面板在滚动面板JScrollPane中显示
		scrollPane.setViewportView(panel);
		// 向JFrame容器窗口中添加JScrollPane滚动面板组件
		f.add(scrollPane, BorderLayout.CENTER);
	}
	public static void main(String[] args) {
	// 使用SwingUtilities工具类调用createAndShowGUI()方法并显示GUI程序
		SwingUtilities.invokeLater(Example11::createAndShowGUI);
	}
}

第11章 GUI_第20张图片

上述代码中,第4~31行定义了createAndShowGUI()方法,在createAndShowGUI()方法中,第6~11行代码创建了一个名为f的容器窗口;第13行代码创建了名为scrollPane滚动面板组件;第15~16行代码设置水平滚动条策略为滚动条需要时显示;第18~19行代码设置垂直滚动条策略为滚动条一直显示;第21~26行代码创建了一个pannel的面板组件,并在pannel的面板组件中添加了四个按钮;第28行代码设置pannnel面板在scrollPane滚动面板中显示。第30行代码向f容器窗口中添加scrollPane滚动面板组件。最后在main()方法中使用SwingUtilities工具类调用封装好的createAndShowGUI()方法并显示GUI程序。

11.5.2 文本组件

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

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

上表列出了文本组件常用的几种操作方法,其中包括选中文本内容、设置文本内容以及获取文本内容等。由于JTextField和JTextArea这两个文本组件继承了JTextComponent类,因此它们都具有上表中的方法。但是在使用上,JtextField和JTextArea还有一定的区别。

1.JTextField

JTextField称为文本框,它只能接收单行文本的输入。 JTextField常用的构造方法如下表。

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

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

2.JTextArea

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

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

接下来通过一个案例演示文本组件JTextField和JTextArea的基本用法,在该案例中,编写一个聊天窗口。

import java.awt.*;
import javax.swing.*;
public class Example12 {
	private static void createAndShowGUI() {
		// 创建一个JFrame聊天窗口
		JFrame f = new JFrame("聊天窗口");
		f.setLayout(new BorderLayout());
		f.setSize(400, 300);
		f.setLocation(300, 200);
		f.setVisible(true);
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		// 创建一个JTextArea文本域,用来显示多行聊天信息
		JTextArea showArea = new JTextArea(12, 34);
		// 创建一个JScrollPane滚动面板组件,将JTextArea文本域作为其显示组件
		JScrollPane scrollPane = new JScrollPane(showArea);
		showArea.setEditable(false); 				// 设置文本域不可编辑
		// 创建一个JTextField文本框,用来输入单行聊天信息
		JTextField inputField = new JTextField(20);
		JButton btn = new JButton("发送"); 
		// 为按钮添加监听事件
		btn.addActionListener(e -> {
			String content = inputField.getText();
			// 判断输入的信息是否为空
			if (content != null && !content.trim().equals("")) {
				// 如果不为空,将输入的文本追加到到聊天窗口
				showArea.append("本人输入信息:" + content + "\n");
			} else {
				// 如果为空,提示聊天信息不能为空
				showArea.append("聊天信息不能为空!!!" + "\n");
			}
			inputField.setText(""); // 将输入的文本域内容置为空
		});
		// 创建一个JPanel面板组件
		JPanel panel = new JPanel();
		JLabel label = new JLabel("聊天信息");   // 创建一个标签
		panel.add(label);                   		// 将标签组件添加到JPanel面板
		panel.add(inputField);             		// 将文本框添加到JPanel面板
		panel.add(btn);                     		// 将按钮添加到JPanel面板
	// 向JFrame聊天窗口的顶部和尾部分别加入面板组件JScrollPane和JPanel
		f.add(scrollPane, BorderLayout.PAGE_START);
		f.add(panel, BorderLayout.PAGE_END);
	}
	public static void main(String[] args) {
	// 使用SwingUtilities工具类调用createAndShowGUI()方法并显示GUI程序
		SwingUtilities.invokeLater(Example12::createAndShowGUI);
	}
}

第11章 GUI_第21张图片

上述代码中,第4~42行代码定义了createAndShowGUI()方法,在createAndShowGUI()方法中,第6~11行代码创建了一个名称为f的容器窗口;第13行代码创建了一个名为showArea的文本域,用于显示多行聊天信息;第15行代码创建了一个名为inputField的滚动面板组件,并将文本域showArea作为显示组件;第16行代码设置showArea文本域不可编辑。第18~19行代码创建了一个名称为inputField的文本框和一个名称为btn的按钮;第24~32行代码判断输入的信息是否为空,如果不为空,将输入的文本发送到聊天窗口;如果为空,提示聊天信息不能为空。文本发送到聊天窗口之后,再将输入的文本域内容置为空。

第34~38行代码创建了一个名称为panel的面板组件,并将需要用到的组件添加到panel 中,第40~41行代码将容器窗口f的顶部和尾部分别加入scrollPane与panel中。最后在main()方法中使用SwingUtilities工具类调用封装好的createAndShowGUI()方法并显示GUI程序。

需要说明的是,文件11-12中第35行代码用到的JLabel组件是一个静态组件,用于显示一行静态文本和图标。它的作用只是信息说明,不接收用户的输入,也不能添加事件,Jlabel的具体用法会在下一小节讲解。

在运行结果的聊天窗口中输入聊天信息,并单击【发送】按钮,结果如右图。

第11章 GUI_第22张图片

11.5.3 标签组件

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

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 实例

接下来通过一个案例演示JLabel标签组件的基本用法。

import java.awt.*;
import javax.swing.*;
public class Example13 {
	private static void createAndShowGUI() {
		// 1、创建一个JFrame容器窗口
		JFrame f = new JFrame("JFrame窗口");
		f.setLayout(new BorderLayout());
		f.setSize(300, 200);
		f.setLocation(300, 200);
		f.setVisible(true);
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		// 2、创建一个JLabel标签组件,用来展示图片
		JLabel label1 = new JLabel();
		// 2.1、创建一个ImageIcon图标组件,并加入JLabel中
		ImageIcon icon = new  ImageIcon("FruitStore.jpg");
		Image img = icon.getImage();
		// 2.2、用于设置图片大小尺寸
		img = img.getScaledInstance(300, 150, Image.SCALE_DEFAULT);  
		icon.setImage(img);  
		label1.setIcon(icon);
		// 3、创建一个页尾JPanel面板,并加入JLabel标签组件
		JPanel panel = new JPanel();
		JLabel label2 = new JLabel("欢迎进入水果超市",JLabel.CENTER);
		panel.add(label2);
		// 4、向JFrame聊天窗口容器的顶部和尾部分别加入JLabel和JPanel组件
		f.add(label1, BorderLayout.PAGE_START);
		f.add(panel, BorderLayout.PAGE_END);
	}
	public static void main(String[] args) {
	// 使用SwingUtilities工具类调用createAndShowGUI()方法并显示GUI程序
		SwingUtilities.invokeLater(Example13::createAndShowGUI);
	}
}

第11章 GUI_第23张图片

上述代码中,第6行代码是使用JFrame顶级容器创建了一个窗口容器。第7~11行代码依次设置了窗口容器的布局模式、窗口大小、位置、是否可见以及设置用户在此窗体上发起 "close" 时执行关闭操作。第13~24行代码创建了标签组件lable1、lable2、图标组件icon和面板组件panel,并分别将图标组件icon和面板组件panel添加到标签组件lable1、lable2中,其中,ImageIcon图标组件用来显示背景图片。第26~27行代码通过BorderLayout布局管理器将窗口分为上下两个区域。

11.5.4 按钮组件

Swing常用的按钮组件有JButton、JCheckBox、JRadioButton等,它们都是抽象类AbstractButton类的直接或间接子类。AbstractButton的常用方法如下表。

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

1.JCheckBox

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

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

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

接下来通过一个案例演示JCheckBox复选框组件的基本用法。

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Example14 {
	private static void createAndShowGUI() {
		// 1、创建一个JFrame容器窗口
		JFrame f = new JFrame("JFrame窗口");
		f.setLayout(new BorderLayout());
		f.setSize(300, 300);
		f.setLocation(300, 200);
		f.setVisible(true);
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		// 2、创建一个JLabel标签组件,标签文本居中对齐
		JLabel label = new JLabel("Hello World!", JLabel.CENTER);
		label.setFont(new Font("宋体", Font.PLAIN, 20));
		// 3、创建一个JPanel面板组件
		JPanel panel = new JPanel();
		// 3.1、创建两个JCheckBox复选框,并添加到JPanel组件中
		JCheckBox italic = new JCheckBox("ITALIC");
		JCheckBox bold = new JCheckBox("BOLD");
		// 3.2、为复选框定义ActionListener监听器
		ActionListener listener = new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				int mode = 0;
				if (bold.isSelected())
					mode += Font.BOLD;
				if (italic.isSelected())
					mode += Font.ITALIC;
				label.setFont(new Font("宋体", mode, 20));
			}
		};
		// 3.3、为两个复选框添加监听器
		italic.addActionListener(listener);
		bold.addActionListener(listener);
		// 3.4、在JPanel面板添加复选框
		panel.add(italic);
		panel.add(bold);
	// 4、向JFrame窗口容器中加入居中的JLabel标签组件和页尾的JPanel面板组件
		f.add(label);
		f.add(panel, BorderLayout.PAGE_END);
	}
	public static void main(String[] args) {
	// 使用SwingUtilities工具类调用createAndShowGUI()方法并显示GUI程序
		SwingUtilities.invokeLater(Example14::createAndShowGUI);
	}
}

第11章 GUI_第24张图片

上述代码中,第7~12行代码使用JFrame顶级容器创建并设置了一个容器窗口,第8行代码通过BorderLayout布局管理器将窗口分为上下两个区域,第36~37行代码在页尾JPanel面板组件中又添加了两个JCheckBox复选框组件,第39~40行代码分别加入了居中的JLabel标签组件和页尾的JPanel面板组件,同时在第33~34行代码中针对两个不同的复选框组件还添加了动作监听器。

在运行结果图中,从左到右分别表示未勾选、勾选一个和勾选两个复选框时,JLabel标签组件中的内容“Hello World!”所显示的字体样式。

2.JRadionButton

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

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

JRadioButton的常用构造方法如下表。

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

接下来通过一个案例演示JRadioButton单选按钮组件的基本用法。

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Example15 {
	private static void createAndShowGUI() {
		// 1、创建一个JFrame容器窗口
		JFrame f = new JFrame("JFrame窗口");
		f.setLayout(new BorderLayout());
		f.setSize(300, 300);
		f.setLocation(300, 200);
		f.setVisible(true);
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		// 2、创建一个JLabel标签组件,标签文本居中对齐
		JLabel label = new JLabel("Hello World!",JLabel.CENTER);
		label.setFont(new Font("宋体", Font.PLAIN, 20));
		// 3、创建一个页尾的JPanel面板组件,来封装ButtonGroup组件
		JPanel panel = new JPanel();
		// 3.1、创建一个ButtonGroup按钮组件
		ButtonGroup group = new ButtonGroup();
		// 3.2、创建二个JRadioButton单选按钮组件
		JRadioButton italic = new JRadioButton("ITALIC");
		JRadioButton bold = new JRadioButton("BOLD");
		// 3.3、将二个JRadioButton单选按钮组件加入到同一个ButtonGroup组中
		group.add(italic);
		group.add(bold);
		// 3.4、为二个JRadioButton单选按钮组件注册动作监听器
		ActionListener listener = new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				int mode = 0;
				if (bold.isSelected())
					mode += Font.BOLD;
				if (italic.isSelected())
					mode += Font.ITALIC;
				label.setFont(new Font("宋体", mode, 20));
			}
		};
		// 3.5、为二个单选框添加监听器
		italic.addActionListener(listener);
		bold.addActionListener(listener);
		// 3.6、将二个JRadioButton单选按钮组件加入到页尾的JPanel组件中
		panel.add(italic);
		panel.add(bold);
		// 4、向JFrame容器中分别加入居中的JLabel标签组件和页尾的JPanel面板组件
		f.add(label);
		f.add(panel, BorderLayout.PAGE_END);
	}
	public static void main(String[] args) {
		// 使用SwingUtilities工具类调用createAndShowGUI()方法显示GUI程序
		SwingUtilities.invokeLater(Example15::createAndShowGUI);
	}
}

第11章 GUI_第25张图片

上述代码中,第7~12行代码使用JFrame顶级容器创建一个窗口容器并设置了窗口容器的布局模式、窗口大小、位置、是否可见以及用户在此窗体上发起 "close" 时执行关闭操作。第14~15行代码创建了标签组件lable1并设置样式;第17行代码创建了一个面板组件,第19~25行代码依次创建了一个ButtonGroup按钮组件和两个JRadioButton单选按钮组件,并将这两个JRadioButton单选按钮组件加入到ButtonGroup按钮组件中。第27~42行代码为两个JRadioButton单选按钮组件注册动作监听器,并设置选择不同的JRadioButton单选按钮时标签组件lable1显示不同的效果。

11.5.5 下拉框组件

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

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

JComboBox的常用构造方法如下表。

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

除了构造方法,JComboBox还提供了很多成员方法,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类的构造方法和常用方法,接下来通过一个案例演示该组件的基本用法。

import java.awt.*;
import javax.swing.*;
public class Example16 {
	private static void createAndShowGUI() {
		// 1、创建一个JFrame容器窗口
		JFrame f = new JFrame("JFrame窗口");
		f.setLayout(new BorderLayout());
		f.setSize(350, 200);
		f.setLocation(300, 200);
		f.setVisible(true);
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		// 2、创建一个页头的JPanel面板,用来封装JComboBox下拉框组件
		JPanel panel = new JPanel();
		// 2.1、创建JComboBox下拉框组件
		JComboBox comboBox = new JComboBox<>();
		// 2.2、为下拉框添加选项
		comboBox.addItem("请选择城市");
		comboBox.addItem("北京");
		comboBox.addItem("天津");
		comboBox.addItem("南京");
		comboBox.addItem("上海");
		// 2.3、创建JTextField单行文本框组件,用来展示用户选择项
		JTextField textField = new JTextField(20);
		// 2.4、为JComboBox下拉框组件注册动作监听器
		comboBox.addActionListener(e -> {
			String item = (String) comboBox.getSelectedItem();
			if ("请选择城市".equals(item)) {
				textField.setText("");
			} else {
				textField.setText("您选择的城市是:" + item);
			}
		});
		// 2.5、将JComboBox组件和JTextField组件加入JPanel面板组件中
		panel.add(comboBox); 
		panel.add(textField); 
		// 3、向JFrame窗口容器中加入页头的JPanel面板组件
		f.add(panel, BorderLayout.PAGE_START);
	}
	public static void main(String[] args) {
	// 使用SwingUtilities工具类调用createAndShowGUI()方法并显示GUI程序
		SwingUtilities.invokeLater(Example16::createAndShowGUI);
	}
}

第11章 GUI_第26张图片

上述代码中,第6~11行代码使用JFrame顶级容器创建并设置了一个容器窗口,在第7行代码通过BorderLayout布局管理器进行布局设置,第13行代码在容器页头加入了一个JPanel面板组件,第13~23行代码在JPanel面板组件中分别封装了一个JComboBox下拉框组件和一个JTextField文本框组件,并在第25~32行代码为JComboBox组件注册了事件监听器,在34~35行代码将JComboBox组件和JTextField组件加入JPanel面板组件中,在第37行代码中向JFrame窗口容器中加入页头的JPanel面板组件。

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

1.下拉式菜单

对于下拉式菜单,大家肯定很熟悉,因为计算机中很多文件的菜单都是下拉式的,如记事本的菜单。Swing提供了三个组件用于创建下拉式菜单,这三个组件分别是JmenuBar(菜单栏)、Jmenu(菜单)和JmenuItem(菜单项)。

以记事本为例,这三个组件在下拉式菜单中对应的位置如下图。

第11章 GUI_第27张图片

(1)JMenuBar:JMenuBar表示一个水平的菜单栏,用来管理一组菜单,不参与用户的交互式操作。菜单栏可以放在容器的任何位置,但通常情况下会使用顶级容器(如JFrame、Jdialog)的setJMenuBar()方法将菜单栏放置在顶级容器的顶部。

JMenuBar有一个无参构造方法,创建菜单栏时,只需要使用new关键字创建JMenubar对象即可。创建完菜单栏对象后,通过对象调用add(JMenu c)方法为菜单栏添加JMenu菜单。

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

除了构造方法,JMenu中还提供了一些常用的方法,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() 从菜单中移除所有的菜单项

(3)JMenuItem:JMenuItem表示一个菜单项,它是下拉式菜单系统中最基本的组件。在创建JMenuItem菜单项时,通常使用构造方法JMenumItem(String text)为菜单项指定文本内容。

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

接下来通过一个案例学习下拉式菜单组件的基本用法。

import javax.swing.*;
public class Example17 {
	private static void createAndShowGUI() {
		// 1、创建一个JFrame容器窗口
		JFrame f = new JFrame("JFrame窗口");
		f.setSize(350, 300);
		f.setLocation(300, 200);
		f.setVisible(true);
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		// 2、创建菜单栏组件JMenuBar
		JMenuBar menuBar = new JMenuBar(); 
		// 2.1、创建2个JMenu菜单组件,并加入JMenuBar中
		JMenu menu1 = new JMenu("文件(F)");
		JMenu menu2 = new JMenu("帮助(H)");
		menuBar.add(menu1);
		menuBar.add(menu2);
		// 2.2、创建2个JMenuItem菜单项组件,并加入JMenu中
		JMenuItem item1 = new JMenuItem("新建(N)");
		JMenuItem item2 = new JMenuItem("退出(X)");
		menu1.add(item1);
		menu1.addSeparator();   // 设置分隔符
		menu1.add(item2);
		// 2.3、分别创建2个JMenuItem菜单项监听器
		item1.addActionListener(e -> {
			// 创建一个JDialog弹窗
			JDialog dialog = new JDialog(f,"无标题",true);
			dialog.setSize(200, 100);
			dialog.setLocation(300, 200);
			dialog.setVisible(true);
			dialog.setDefaultCloseOperation(JDialog.HIDE_ON_CLOSE);
		});
		item2.addActionListener(e -> System.exit(0));
		// 3、向JFrame窗口容器中加入JMenuBar菜单组件
		f.setJMenuBar(menuBar);
	}
	public static void main(String[] args) {
	// 使用SwingUtilities工具类调用createAndShowGUI()方法并显示GUI程序
		SwingUtilities.invokeLater(Example17::createAndShowGUI);
	}
}

第11章 GUI_第28张图片

上述代码中,第5~9行代码使用JFrame顶级容器创建一个窗口容器并设置了窗口容器的布局模式、窗口大小、位置、是否可见以及用户在此窗体上发起 “close” 时执行关闭操作。第11行代码创建菜单栏组件JMenuBar,第13~16行代码创建2个JMenu菜单组件 “文件”和“帮助”并加入JMenuBar中,第18~22行代码创建了2个JMenuItem菜单项组件“新建”和“退出”并加入JMenu中,第24~31行代码是为“新建”菜单项添加监听器,第32行代码是为“退出”菜单项添加监听器。

2.弹出式菜单

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

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

接下来通过一个案例演示JPopupMenu组件的用法。

import java.awt.event.*;
import javax.swing.*;
public class Example18 {
	private static void createAndShowGUI() {
		// 1、创建一个JFrame容器窗口
		JFrame f = new JFrame("JFrame窗口");
		f.setSize(300, 200);
		f.setLocation(300, 200);
		f.setVisible(true);
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		// 2、创建JPopupMenu弹出式菜单
		JPopupMenu popupMenu = new JPopupMenu();
		// 2.1、创建两个JMenuItem菜单项,并加入到JPopupMenu组件中
		JMenuItem item1 = new JMenuItem("查看");
		JMenuItem item2 = new JMenuItem("刷新");
		popupMenu.add(item1);
		popupMenu.addSeparator();
		popupMenu.add(item2);
		// 3、为JFrame窗口添加鼠标事件监听器
		f.addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent e) {
				// 如果单击的是鼠标右键,显示JPopupMenu菜单
			  if (e.getButton() == MouseEvent.BUTTON3) {
				popupMenu.show(e.getComponent(), e.getX(), e.getY());
			   }
			}
		});
	}
	public static void main(String[] args) {
	// 使用SwingUtilities工具类调用createAndShowGUI()方法并显示GUI程序
		SwingUtilities.invokeLater(Example18::createAndShowGUI);
	}
}

第11章 GUI_第29张图片

上述代码中,第6~10行代码先定义了一个JFrame容器窗口,并设置了窗口容器的布局模式、窗口大小、位置、是否可见以及用户在此窗体上发起 "close" 时执行关闭操作。第12~18行代码使用JPopupMenu创建并设置了一个弹出式菜单,并为该菜单添加了2个JMenuItem菜单项,分别是“查看”和刷新。由于JPopupMenu菜单默认情况下是不显示的,因此第20~27行代码为JFrame窗口注册了一个鼠标事件监听器,当单击鼠标右键时,显示JPopupMenu菜单。

【案例11-1】 简易记事本

本案例要求利用Java Swing 图形组件开发一个图形化简易记事本。记事本功能包括文本编辑、保存文本到指定路径、打开指定路径下的文本、退出等。简易记事本效果如右图。

第11章 GUI_第30张图片

(1)记事本界面整体可以看做是一个容器窗口。

(2)从运行结果中的记事本界面的布局效果显示内容可以看出,该图形化界面有最小化、放大缩小以及关闭按钮,以及菜单栏、菜单、文本域。菜单栏可以使用JMenuBar实现,文本域可以使用JTextArea来实现,菜单可以使用JMenu来实现,菜单项可以使用JMenuItem来实现。

(3)为了使窗口可以最小化、放大缩小、关闭还必须为这些按钮注册监听器,进行相应的事件处理。

(4)定义一个程序入口,用于启动Swing案例程序。

 1 	import java.awt.*;
 2 	import java.awt.event.ActionEvent;
 3 	import java.awt.event.ActionListener;
 4 	import java.io.*;
 5 	import javax.swing.*;
 6 	class MyNotePad extends JFrame implements ActionListener {
 7 	 		private JTextArea jta = null;
 8 			private JMenuBar jmb = null;
 9 			private JMenu jm = null;
 10 		private JMenuItem jmiOpen = null;
 11 		private JMenuItem jmiSave = null;
 12 		private JMenuItem jmiExit = null;
 13 	 	private JFileChooser jfc = null;
 14 	 	public MyNotePad() {
 15 			jta = new JTextArea();
 16 			this.setLayout(new BorderLayout());
 17 			this.add(jta);
 18 	 		jmb = new JMenuBar();
 19 			jm = new JMenu("文件");
 20 			jmiOpen = new JMenuItem("打开");
 21 			jmiOpen.addActionListener(this);
 22 			jmiOpen.setActionCommand("打开");
 23 	 		jmiSave = new JMenuItem("保存");
 24 			jmiSave.addActionListener(this);
 25 			jmiSave.setActionCommand("保存");
 26 	 		jmiExit = new JMenuItem("退出");
 27 			jmiExit.addActionListener(this);
 28 			jmiExit.setActionCommand("退出");
 29 	 		jm.add(jmiOpen);
 30 	 		jm.add(jmiSave);
 31 			jm.add(jmiExit);
 32 			jmb.add(jm);
 33 			this.setJMenuBar(jmb);
 34 	 		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
 35 			this.setSize(400, 300);
 36 			this.setVisible(true);
 37 	 	}
 38 	 	@Override
 39 		public void actionPerformed(ActionEvent e) {
 40 			// TODO Auto-generated method stub
 41 			String str = e.getActionCommand();
 42 			if (str.equals("打开")) {
 43 				System.out.println("打开");
 44 				jfc = new JFileChooser();
 45 				jfc.setDialogTitle("请选择文件!");
 46 				jfc.showOpenDialog(null);
 47 				jfc.setVisible(true);
 48 	 			File file = jfc.getSelectedFile();
 49 				BufferedReader br = null;
 50 	 			try {
 51 					FileReader fReader = new FileReader(file);
 52 					br = new BufferedReader(fReader);
 53 					String readStr = "";
 54 					String allCode = "";
 55 					while ((readStr = br.readLine()) != null) {
 56 						allCode += readStr + "\r\n";
 57 					}
 58 					jta.setText(allCode);
 59 	 			} catch (Exception e2) {
 60 					e2.printStackTrace();
 61 					// TODO: handle exception
 62 				} finally {
 63 					try {
 64 						br.close();
 65 					} catch (IOException e1) {
 66 						// TODO Auto-generated catch block
 67 						e1.printStackTrace();
 68 					}
 69 				}
 70 	 		} else if (str.equals("保存")) {
 71 				JFileChooser jfc = new JFileChooser();
 72 				jfc.setDialogTitle("已保存");
 73 				jfc.showSaveDialog(null);
 74 				File file = jfc.getSelectedFile();
 75 				BufferedWriter bw = null;
 76 	 			try {
 77 					FileWriter fw = new FileWriter(file);
 78 					bw = new BufferedWriter(fw);
 79 					String jtaStr = jta.getText();
 80 					bw.write(jtaStr);
 81 				} catch (Exception e2) {
 82 					// TODO: handle exception
 83 					e2.printStackTrace();
 84 				} finally {
 85 					try {
 86 						bw.close();
 87 					} catch (IOException e1) {
 88 						// TODO Auto-generated catch block
 89 						e1.printStackTrace();
 90 					}
 91 				}
 92 	 		} else if (str.equals("退出")) {
 93 				System.exit(0);
 94 			}
 95 		}
 96 	}
 97 	 public class MyMenu {
 98 	 	public static void main(String[] str) {
 99 			MyNotePad notePad = new MyNotePad();
 100 		}
 101 	 
 102 	}

【案例11-2】 简易计算器

本案例要求利用Java Swing 图形组件开发一个可以进行简单的四则运算的图形化计算器。

第11章 GUI_第31张图片

一个简单的图形化界面计算器,需要由界面组件、组件的时间监听器和具体的事件处理逻辑组成。

实现一个简易图形化界面计算器可分以下几个步骤:

1.UI组件创建和初始化:包括窗体的创建,显示计算结果文本框、清除按钮、数字按钮、运算符等按钮的初始化。

2.在窗体中添加UI组件:包括放置数字键及运算符面板、放置清除框面板。

3.布局结束之后,就是计算器的难点:编写事件处理程序。

4.按键的响应实现。

5.计算逻辑的实现。

6.注册监听器

 1 	import java.awt.*;
 2 	import java.awt.event.*;
 3 	import javax.swing.*;
 4 	import java.util.Vector;
 5 	import java.math.BigDecimal;
 6 	public class Calculator {
 7 	          // 操作数1,为了程序的安全,初值一定设置,这里我们设置为0。	
 8 	          String str1 = "0"; 	
 9 		     // 操作数2
 10 	     String str2 = "0"; 
 11 	     // 运算符
 12 		String signal = "+"; 	
 13 	     // 运算结果
 14 		String result = "";
 15 	     // 以下k1至k5为状态开关
 16 	     // 开关1用于选择输入方向,将要写入str1或str2
 17 	     // 为 1 时写入 str1,为 2 时写入 str2
 18 		int k1 = 1;
 19 	    // 开关 2 用于记录符号键的次数
 20 	     // 如果 k2>1 说明进行的是 2+3-9+8 这样的多符号运算
 21 		int k2 = 1;
 22 	     // 开关3用于标识 str1 是否可以被清 0 
 23 	     // 等于 1 时可以,不等于1时不能被清0
 24 		int k3 = 1;
 25 	     // 开关4用于标识 str2 是否可以被清 0
 26 	     // 等于 1 时可以,不等于1时不能被清0
 27 		int k4 = 1;
 28 	     // 开关5用于控制小数点可否被录入
 29 	     // 等于1时可以,不为1时,输入的小数点被丢掉
 30 		int k5 = 1;
 31 	     // store的作用类似于寄存器,用于记录是否连续按下符号键
 32 		JButton store; 
 33 	     //vt 存储之前输入的运算符。
 34 		@SuppressWarnings("rawtypes")
 35 		Vector vt = new Vector(20, 10);
 36 	     // 创建一个 JFrame 对象并初始化。JFrame 可以理解为程序的主窗体。	
 37 	     JFrame frame = new JFrame("Calculator");
 38 	 //创建一个JTextField对象并初始化。JTextField用于显示操作和计算结果的文本框。
 39 	     // 参数 20 表明可以显示 20 列的文本内容
 40 		JTextField result_TextField = new JTextField(result, 20);
 41 	     // 清除按钮
 42 		JButton clear_Button = new JButton("Clear");
 43 	     // 数字键0到9
 44 		JButton button0 = new JButton("0");
 45 		JButton button1 = new JButton("1");
 46 		JButton button2 = new JButton("2");
 47 		JButton button3 = new JButton("3");
 48 		JButton button4 = new JButton("4");
 49 		JButton button5 = new JButton("5");
 50 		JButton button6 = new JButton("6");
 51 		JButton button7 = new JButton("7");
 52 		JButton button8 = new JButton("8");
 53 		JButton button9 = new JButton("9");
 54 	     // 计算命令按钮,加减乘除以及小数点等
 55 		JButton button_Dian = new JButton(".");
 56 		JButton button_jia = new JButton("+");
 57 		JButton button_jian = new JButton("-");
 58 		JButton button_cheng = new JButton("*");
 59 		JButton button_chu = new JButton("/");
 60 		JButton button_dy = new JButton("=");
 61 		public Calculator() {
 62 			button0.setMnemonic(KeyEvent.VK_0);
 63 			result_TextField.setHorizontalAlignment(JTextField.RIGHT);
 64 			// 创建一个 Jpanel 对象并初始化
 65 	         JPanel pan = new JPanel();
 66 	         // 设置该容器的布局为四行四列,边距为5像素
 67 			pan.setLayout(new GridLayout(4, 4, 5, 5));
 68 	         // 将用于计算的按钮添加到容器内
 69 			pan.add(button7);
 70 			pan.add(button8);
 71 			pan.add(button9);
 72 			pan.add(button_chu);
 73 			pan.add(button4);
 74 			pan.add(button5);
 75 			pan.add(button6);
 76 			pan.add(button_cheng);
 77 			pan.add(button1);
 78 			pan.add(button2);
 79 			pan.add(button3);
 80 			pan.add(button_jian); 
 81 			pan.add(button0);
 82 			pan.add(button_Dian);
 83 			pan.add(button_dy);
 84 			pan.add(button_jia);
 85 	         // 设置 pan 对象的边距
 86 			pan.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
 87 	         // 按照同样的方式设置第二个JPanel
 88 			JPanel pan2 = new JPanel();
 89 			pan2.setLayout(new BorderLayout());
 90 			pan2.add(result_TextField, BorderLayout.WEST);
 91 			pan2.add(clear_Button, BorderLayout.EAST);
 92 	          // 设置主窗口出现在屏幕上的位置
 93 			frame.setLocation(300, 200);
 94 	         // 设置窗体可以调大小
 95 			frame.setResizable(true); 
 96 	         //窗体中可以放置 JPanel,这里我们将面板pan和面板pan2让如窗体
 97 			frame.getContentPane().setLayout(new BorderLayout());
 98 			frame.getContentPane().add(pan2, BorderLayout.NORTH);
 99 			frame.getContentPane().add(pan, BorderLayout.CENTER);
 100 			frame.pack();
 101 			frame.setVisible(true);
 102 	         //Listener类中编写的是数字键的响应逻辑。
 103 			class Listener implements ActionListener {
 104 				@SuppressWarnings("unchecked")
 105 				public void actionPerformed(ActionEvent e) {
 106 					String ss = ((JButton) e.getSource()).getText();
 107 					store = (JButton) e.getSource();
 108 					vt.add(store);
 109 					if (k1 == 1) {
 110 						if (k3 == 1) {
 111 							str1 = "";
 112 							k5 = 1;
 113 						}
 114 						str1 = str1 + ss;
 115 						k3 = k3 + 1;
 116 						result_TextField.setText(str1);
 117 					} else if (k1 == 2) {
 118 						if (k4 == 1) {
 119 							str2 = "";
 120 							k5 = 1; 
 121 					}
 122 						str2 = str2 + ss;
 123 						k4 = k4 + 1;
 124 						result_TextField.setText(str2);
 125 					}                              
 126 				}
 127 			}
 128 	          //Listener_signal类中编写了运算符号键的响应逻辑
 129 			class Listener_signal implements ActionListener {
 130 				@SuppressWarnings("unchecked")
 131 				public void actionPerformed(ActionEvent e) {
 132 					String ss2 = ((JButton) e.getSource()).getText();
 133 					store = (JButton) e.getSource();
 134 					vt.add(store);
 135 					if (k2 == 1) {
 136 						k1 = 2;
 137 						k5 = 1;
 138 						signal = ss2;
 139 						k2 = k2 + 1;
 140 					} else {
 141 						int a = vt.size();
 142 						JButton c = (JButton) vt.get(a - 2);
 143 						if (!(c.getText().equals("+"))
 144 								&& !(c.getText().equals("-"))
 145 								&& !(c.getText().equals("*"))
 146 								&& !(c.getText().equals("/")))
 147 						{
 148 							cal();
 149 							str1 = result;
 150 							k1 = 2;
 151 							k5 = 1;
 152 							k4 = 1;
 153 							signal = ss2;
 154 						}
 155 						k2 = k2 + 1;
 156 					}
 157 				}
 158 			}
 159 	     //Listener_clear类中编写了清除键的响应逻辑
 160 		class Listener_clear implements ActionListener {
 161 				@SuppressWarnings("unchecked")
 162 				public void actionPerformed(ActionEvent e) {
 163 					store = (JButton) e.getSource();
 164 					vt.add(store);
 165 					k5 = 1;
 166 					k2 = 1;
 167 					k1 = 1;
 168 					k3 = 1;
 169 					k4 = 1;
 170 					str1 = "0";
 171 					str2 = "0";
 172 					signal = "";
 173 					result = "";
 174 					result_TextField.setText(result);
 175 					vt.clear();
 176 				}
 177 			}
 178 	     //Listener_dy类中编写的是等于号键的响应逻辑
 179 		class Listener_dy implements ActionListener {
 180 				@SuppressWarnings("unchecked")
 181 				public void actionPerformed(ActionEvent e) {
 182 					store = (JButton) e.getSource();
 183 					vt.add(store);
 184 					cal();
 185 					k1 = 1; 
 186 					k2 = 1;
 187 					k3 = 1;
 188 					k4 = 1;
 189 					str1 = result; 
 190 				}
 191 			}
 192 	//Listener_xiaos类中编写的是小数点键的相应逻辑
 193 	class Listener_xiaos implements ActionListener {
 194 				@SuppressWarnings("unchecked")
 195 				public void actionPerformed(ActionEvent e) {
 196 					store = (JButton) e.getSource();
 197 					vt.add(store);
 198 					if (k5 == 1) {
 199 						String ss2 = ((JButton) e.getSource()).getText();
 200 						if (k1 == 1) {
 201 							if (k3 == 1) {
 202 								str1 = "";
 203 								k5 = 1; 
 204 							}
 205 							str1 = str1 + ss2;
 206 							k3 = k3 + 1;
 207 							result_TextField.setText(str1);
 208 						} else if (k1 == 2) {
 209 							if (k4 == 1) {
 210 								str2 = "";
 211 								k5 = 1;
 212 							}
 213 							str2 = str2 + ss2;
 214 							k4 = k4 + 1;
 215 							result_TextField.setText(str2);
 216 						}
 217 					}
 218 					k5 = k5 + 1;
 219 				}
 220 			}
 221 	          // 监听等于键
 222 			Listener_dy jt_dy = new Listener_dy();
 223 	         // 监听数字键
 224 			Listener jt = new Listener();
 225 	         // 监听符号键
 226 			Listener_signal jt_signal = new Listener_signal();
 227 	         // 监听清除键
 228 			Listener_clear jt_c = new Listener_clear(); 
 229 	         // 监听小数点键
 230 			Listener_xiaos jt_xs = new Listener_xiaos();
 231 			button7.addActionListener(jt);
 232 			button8.addActionListener(jt);
 233 			button9.addActionListener(jt);
 234 			button_chu.addActionListener(jt_signal);
 235 			button4.addActionListener(jt);
 236 			button5.addActionListener(jt);
 237 			button6.addActionListener(jt);
 238 			button_cheng.addActionListener(jt_signal);
 239 			button1.addActionListener(jt);
 240 			button2.addActionListener(jt);
 241 			button3.addActionListener(jt);
 242 			button_jian.addActionListener(jt_signal);
 243 			button0.addActionListener(jt);
 244 			button_Dian.addActionListener(jt_xs);
 245 			button_dy.addActionListener(jt_dy);
 246 			button_jia.addActionListener(jt_signal);
 247 			clear_Button.addActionListener(jt_c);
 248 	          // 窗体关闭事件的响应程序
 249 			frame.addWindowListener(new WindowAdapter() {
 250 				public void windowClosing(WindowEvent e) {
 251 					System.exit(0);
 252 				}
 253 			});
 254 		}
 255 	     // calc()方法中编写了计算逻辑的实现。
 256 		public void cal() {
 257 			double a2;
 258 			double b2;
 259 			String c = signal;
 260 			double result2 = 0;
 261 			if (c.equals("")) {
 262 				result_TextField.setText("Please input operator");
 263 			} else {
 264 				if (str1.equals("."))
 265 					str1 = "0.0";
 266 				if (str2.equals("."))
 267 					str2 = "0.0";
 268 				a2 = Double.valueOf(str1).doubleValue();
 269 				b2 = Double.valueOf(str2).doubleValue();
 270 				if (c.equals("+")) {
 271 					result2 = a2 + b2;
 272 				}
 273 				if (c.equals("-")) {
 274 					result2 = a2 - b2;
 275 				}
 276 				if (c.equals("*")) {
 277 					BigDecimal m1 = new BigDecimal(Double.toString(a2));
 278 				        BigDecimal m2 = new
 279 	                                    BigDecimal(Double.toString(b2));
 280 				        result2 = m1.multiply(m2).doubleValue();
 281 				}
 282 				if (c.equals("/")) {
 283 					if (b2 == 0) {
 284 						result2 = 0;
 285 					} else {
 286 						result2 = a2 / b2;
 287 					}
 288 				}
 289 				result = ((new Double(result2)).toString());
 290 				result_TextField.setText(result);
 291 			}
 292 		}
 293 		@SuppressWarnings("unused")
 294 		public static void main(String[] args) {
 295 		try {	 
 296 	 //通过 UIManager 来设置窗体的 UI 风格
 297 	 UIManager.setLookAndFeel("javax.swing.plaf.metal.MetalLookAndFeel");
 298 			} catch (Exception e) {
 299 				e.printStackTrace();
 300 			}
 301 			Calculator cal = new Calculator();
 302 		}
 303 	}

【案例11-3】 模拟QQ登录

QQ是现实生活中常用的聊天工具,QQ登录界面看似小巧、简单,但其中涉及的内容却很多,对于初学者练习Java Swing工具的使用非常合适。本案例要求使用所学的Java Swing知识,模拟实现一个QQ登录界面。

第11章 GUI_第32张图片

(1)首先,需要定义一些成员变量,如最小化、关闭、账号、密码、头像等,方便响应的逻辑实现。

(2)由于需要对账号、密码、头像等进行布局,故需要先对这些对象进行实例化。

(3)在对需要用到的文本框、图片等对象进行实例化过后,可以使用对象.setBounds()设置文本框、图片等组件的位置。

(4)接下来,对最小化、关闭、账号、密码、头像等添加监听事件。同时,对窗体也添加窗体拖动监听事件。

(5)最后,为最小化、关闭等编写点击时的执行逻辑。为账号、密码等设置点击、悬停等执行逻辑。

 1 	import java.awt.Color;
 2 	    import java.awt.Cursor;
 3 	    import java.awt.Font;
 4 	    import java.awt.Point;
 5 	    import java.awt.Toolkit;
 6 	    import java.awt.event.FocusEvent;
 7 	    import java.awt.event.FocusListener;
 8 	    import java.awt.event.MouseEvent;
 9 	    import java.awt.event.MouseListener;
 10 	import java.awt.event.MouseMotionListener;
 11 	import javax.swing.ImageIcon;
 12 	import javax.swing.JFrame;
 13 	import javax.swing.JLabel;
 14 	import javax.swing.JOptionPane;
 15 	import javax.swing.JPanel;
 16 	import javax.swing.JPasswordField;
 17 	import javax.swing.JTextField;
 18 	public class Login extends JFrame implements MouseListener {
 19 		JLabel bacgrangd, jan,bi,QQ,qq,tu;//gif,最小化,关闭,logo,QQ,头像
 20 		JLabel an1, an2, lie1, lie2;// 暗色块|线
 21 		JTextField user;// 账号
 22 		JPasswordField pass;// 密码
 23 		JPanel bgcolor;// 白
 24 		JLabel su1, mi1, ku1, ku2, gou1, gou2;// 缩略图
 25 		JLabel text1, text2, text3, text4, text5;//自动登录,记住密码,找回
 26 	密码,注册账号,登录
 27 		static Point origin = new Point();// 变量,用于可拖动窗体
 28 		int a = 0, b = 0, c = 0, d = 0;// 控制线
 29 		int f = 0, g = 0, h = 0, j = 0;// 控制√
 30 		JLabel submit, ma;// 背景
 31 		public Login() {
 32 			//实例化
 33 			bacgrangd = new JLabel(new ImageIcon("images/1.gif"));
 34 			jan = new JLabel(new ImageIcon("images/最小化.png"));
 35 			bi = new JLabel(new ImageIcon("images/关闭.png"));
 36 			QQ = new JLabel(new ImageIcon("imagesqq.png"));
 37 			qq = new JLabel("QQ");
 38 			an1 = new JLabel();
 39 			an2 = new JLabel();// 暗调
 40 			tu = new JLabel(new ImageIcon("images/头像.png"));
 41 			user = new JTextField();
 42 			pass = new JPasswordField();
 43 			su1 = new JLabel(new ImageIcon("images/qq (1).png"));
 44 			mi1 = new JLabel(new ImageIcon("images/密码.png"));
 45 			lie1 = new JLabel(new ImageIcon("images/直线2.png"));
 46 			lie2 = new JLabel(new ImageIcon("images/直线2.png"));
 47 			bgcolor = new JPanel();
 48 			ku1 = new JLabel(new ImageIcon("images/框框.png"));
 49 			ku2 = new JLabel(new ImageIcon("images/框框.png"));
 50 			gou1 = new JLabel(new ImageIcon("images/对勾.png"));
 51 			gou2 = new JLabel(new ImageIcon("images/对勾.png"));
 52 			text1 = new JLabel("自动登录");
 53 			text2 = new JLabel("记住密码");
 54 			text3 = new JLabel("找回密码");
 55 			text4 = new JLabel("注册账号");
 56 			text5 = new JLabel("登录");
 57 			submit = new JLabel();
 58 			ma = new JLabel(new ImageIcon("images/二维码.png"));
 59 			//位置
 60 			bacgrangd.setBounds(-35, -123, 500, 250);
 61 			jan.setBounds(364, 2, 32, 32);
 62 			bi.setBounds(396, 3, 32, 32);
 63 			QQ.setBounds(10, 10, 32, 32);
 64 			qq.setBounds(50, 5, 45, 45);
 65 			an1.setBounds(361, 0, 35, 35);
 66 			an2.setBounds(395, 0, 35, 35);
 67 			tu.setBounds(170, 80, 90, 85);
 68 			user.setBounds(130, 160, 180, 40);
 69 			pass.setBounds(130, 200, 180, 40);
 70 			su1.setBounds(100, 170, 20, 20);
 71 			mi1.setBounds(100, 210, 20, 20);
 72 			lie1.setBounds(100, 190, 240, 10);
 73 			lie2.setBounds(100, 230, 240, 10);
 74 			bgcolor.setBounds(0, 125, 500, 300);
 75 			ku1.setBounds(100, 250, 20, 20);
 76 			ku2.setBounds(190, 250, 20, 20);
 77 			gou1.setBounds(106, 255, 10, 10);
 78 			gou2.setBounds(196, 255, 10, 10);
 79 			text1.setBounds(125, 250, 80, 20);
 80 			text2.setBounds(215, 250, 80, 20);
 81 			text3.setBounds(288, 250, 80, 20);
 82 			text4.setBounds(15, 300, 80, 20);
 83 			text5.setBounds(206, 285, 80, 20);
 84 			submit.setBounds(100, 280, 242, 35);
 85 			ma.setBounds(385, 290, 30, 30);
 86 			//属性
 87 			qq.setFont(new Font("微软雅黑", 1, 25));
 88 			qq.setForeground(Color.white);
 89 			an1.setBackground(new Color(0, 0, 0, 0.3f));
 90 			an2.setBackground(new Color(0, 0, 0, 0.3f));
 91 			bgcolor.setBackground(new Color(255, 255, 255));
 92 			user.setForeground(Color.gray);
 93 			user.setText("QQ号码/手机/邮箱");
 94 			user.setOpaque(false);// 透明背景
 95 			user.setBorder(null);// 去掉边框
 96 	         // 框内文字样式
 97 			user.setFont(new Font("微软雅黑", Font.PLAIN, 16)); 
 98 	         // 框内文字样式
 99 			pass.setFont(new Font("微软雅黑", Font.PLAIN, 16)); 
 100 			pass.setBorder(null);// 去掉边框
 101 			pass.setOpaque(false);// 透明背景
 102 			pass.setForeground(Color.gray);
 103 			pass.setText("密码");
 104 			pass.setEchoChar((char) 0);// 让密码显示出来
 105 			text1.setFont(new Font("微软雅黑", 0, 12));
 106 			text2.setFont(new Font("微软雅黑", 0, 12));
 107 			text3.setFont(new Font("微软雅黑", 0, 12));
 108 			text4.setFont(new Font("微软雅黑", 0, 12));
 109 			text5.setFont(new Font("微软雅黑", 0, 15));
 110 			text1.setForeground(new Color(170, 170, 170));
 111 			text2.setForeground(new Color(170, 170, 170));
 112 			text3.setForeground(new Color(170, 170, 170));
 113 			text4.setForeground(new Color(170, 170, 170));
 114 			text5.setForeground(Color.white);
 115 			gou1.setVisible(false);
 116 			gou2.setVisible(false);
 117 			submit.setBackground(new Color(5, 186, 251));
 118 			submit.setOpaque(true);
 119 		 text3.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
 120 		 text4.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
 121 			//事件区域
 122 			jan.addMouseListener(this);
 123 			bi.addMouseListener(this);
 124 			user.addMouseListener(this);
 125 			pass.addMouseListener(this);
 126 			text1.addMouseListener(this);
 127 			text2.addMouseListener(this);
 128 			text3.addMouseListener(this);
 129 			text4.addMouseListener(this);
 130 			ku1.addMouseListener(this);
 131 			ku2.addMouseListener(this);
 132 			submit.addMouseListener(this);
 133 			ma.addMouseListener(this);
 134 			this.addMouseListener(this);
 135 	         // 窗体拖动事件
 136 	 this.addMouseMotionListener(new MouseMotionListener() { 
 137 				public void mouseMoved(MouseEvent e) {
 138 			}
 139 		public void mouseDragged(MouseEvent e) {
 140 		   Point p = getLocation();
 141 	        setLocation(p.x + e.getX()-origin.x, p.y + e.getY()-origin.y);
 142 		}
 143 	});
 144 	user.addFocusListener(new FocusListener() {
 145 	     public void focusLost(FocusEvent e) {// 失去焦点
 146 			 su1.setIcon(new javax.swing.ImageIcon("images/qq (1).png"));
 147 			 lie1.setIcon(new javax.swing.ImageIcon("images/直线2.png"));
 148 				  c = 0;
 149 	              // 判断是否为空(为了设置默认提示语)
 150 		         if (user.getText().isEmpty()) {
 151 						user.setForeground(Color.gray);
 152 						user.setText("QQ号码/手机/邮箱");
 153 					}
 154 				}
 155 	        // 得到焦点
 156 		   public void focusGained(FocusEvent e) { 
 157 			 user.setForeground(Color.black);
 158 			 lie1.setIcon(new javax.swing.ImageIcon("images/直线3.png"));
 159 					a = 1;
 160 					c = 1;
 161 					b = 0;
 162 			  su1.setIcon(new javax.swing.ImageIcon("images/qq(2).png"));
 163 					if (user.getText().equals("QQ号码/手机/邮箱")) {
 164 						user.setText("");
 165 					} else {
 166 						user.setText(user.getText());
 167 						user.selectAll();
 168 					}
 169 				}
 170 			});
 171 			pass.addFocusListener(new FocusListener() {
 172 	            // 失去焦点
 173 			   public void focusLost(FocusEvent e) { 
 174 	            // 失去焦点换图片
 175 			   lie2.setIcon(new javax.swing.ImageIcon("images/直2.png")); 
 176 			   mi1.setIcon(new javax.swing.ImageIcon("images/密码.png"));
 177 					d = 0;
 178 					if (pass.getText().isEmpty()) {
 179 						pass.setForeground(Color.gray);
 180 						pass.setText("密码");
 181 						pass.setEchoChar((char) 0);// 让密码显示出来
 182 					}
 183 				}
 184 			public void focusGained(FocusEvent e) {// 得到焦点
 185 					mi1.setIcon(new javax.swing.ImageIcon("images/密码
 186 	                  (1).png"));
 187 					lie2.setIcon(new javax.swing.ImageIcon("images/直线
 188 	                  3.png"));
 189 					b = 1;
 190 					a = 0;
 191 					d = 1;
 192 					pass.setForeground(Color.black);
 193 					pass.setEchoChar('*');// 让用户输入看不见
 194 					if (pass.getText().equals("密码")) {
 195 						pass.setText("");
 196 					} else {
 197 						pass.setText(pass.getText());
 198 					}
 199 				}
 200 			});
 201 			this.setLayout(null);// 布局
 202 			this.add(jan);
 203 			this.add(bi);
 204 			this.add(qq);
 205 			this.add(QQ);
 206 			this.add(an1);
 207 			this.add(an2);
 208 			this.add(tu);
 209 			this.add(lie1);
 210 			this.add(lie2);
 211 			this.add(user);
 212 			this.add(pass);
 213 			this.add(su1);
 214 			this.add(mi1);
 215 			this.add(gou1);
 216 			this.add(gou2);
 217 			this.add(ku1);
 218 			this.add(ku2);
 219 			this.add(text1);
 220 			this.add(text2);
 221 			this.add(text3);
 222 			this.add(text4);
 223 			this.add(text5);
 224 			this.add(submit);
 225 			this.add(ma);
 226 			this.add(bgcolor);
 227 			this.add(bacgrangd);
 228 			this.setSize(430, 330);
 229 		this.setIconImage(Toolkit.getDefaultToolkit().createImage("images
 230 	    /透明照片.png"));// 窗体图标
 231 			this.setLocationRelativeTo(null);// 保持居中
 232 			this.setUndecorated(true);// 去顶部
 233 			this.setFocusable(true);// 面板首先获得焦点
 234 			this.setBackground(new Color(255, 255, 255));// 背景颜色
 235 			this.setDefaultCloseOperation(this.EXIT_ON_CLOSE);
 236 			this.setAlwaysOnTop(true);// 最顶层
 237 			this.setVisible(true);// 显示
 238 		}
 239 		public static void main(String[] args) {
 240 			new Login();
 241 		}
 242 	    // 点击不恢复
 243 		public void mouseClicked(MouseEvent e) { 
 244 		}
 245 	    // 点击后
 246 		public void mousePressed(MouseEvent e) { 
 247 			if (e.getSource() == jan) {
 248 				setExtendedState(JFrame.ICONIFIED);
 249 			} else if (e.getSource() == this) {
 250 				origin.x = e.getX();
 251 				origin.y = e.getY();
 252 			} else if (e.getSource() == bi) {
 253 				System.exit(0);
 254 			} else if (e.getSource() == ku1 || e.getSource() == text1) {
 255 				if (f == 0) {
 256 					gou1.setVisible(true);
 257 					g = 1;
 258 					f = 1;
 259 				} else if (g == 1) {
 260 					gou1.setVisible(false);
 261 					f = 0;
 262 					g = 0;
 263 				}
 264 			} else if (e.getSource() == ku2 || e.getSource() == text2) {
 265 				if (h == 0) {
 266 					gou2.setVisible(true);
 267 					j = 1;
 268 					h = 1;
 269 				} else if (j == 1) {
 270 					gou2.setVisible(false);
 271 					h = 0;
 272 					j = 0;
 273 				}
 274 		  } else if (e.getSource() == submit || e.getSource() == text5) {
 275 				text5.setFont(new Font("微软雅黑", 0, 14));
 276 				dispose();
 277 				String users = user.getText();
 278 				String password = pass.getText();
 279 				if (users.equals("itcast") && password.equals("123")) {
 280 			   //new Table();//打开新的主界面如果要关闭登录界面可以写dispose();
 281 				} else {
 282 					JOptionPane.showMessageDialog(null, "用户名:itcast,密
 283 	                   码:123,您并未设置打开界面!");
 284 					new Login();
 285 				}
 286 			}
 287 		}
 288 	     // 点击时
 289 		public void mouseReleased(MouseEvent e) { 
 290 			if (e.getSource() == submit || e.getSource() == text5) {
 291 				text5.setFont(new Font("微软雅黑", 0, 15));
 292 			}
 293 		}
 294 	     // 悬停
 295 		public void mouseEntered(MouseEvent e) { 
 296 			if (e.getSource() == jan) {
 297 				an1.setOpaque(true);
 298 			} else if (e.getSource() == bi) {
 299 				an2.setOpaque(true);
 300 			} else if (e.getSource() == user) {
 301 				if (a == 0 && c == 0) {
 302 			 lie1.setIcon(new javax.swing.ImageIcon("images/直线4.png"));
 303 				}
 304 			} else if (e.getSource() == pass) {
 305 				if (b == 0 && d == 0) {
 306 			 lie2.setIcon(new javax.swing.ImageIcon("images/直线4.png"));
 307 				}
 308 			} else if (e.getSource() == text3) {
 309 				text3.setForeground(Color.GRAY);
 310 			} else if (e.getSource() == text4) {
 311 				text4.setForeground(Color.GRAY);
 312 			} else if (e.getSource() == ma) {
 313 			  ma.setIcon(new javax.swing.ImageIcon("images/二维码2.png"));
 314 			}
 315 		}
 316 		public void mouseExited(MouseEvent e) {// 悬停后
 317 			if (e.getSource() == jan) {
 318 				an1.setOpaque(false);
 319 			} else if (e.getSource() == bi) {
 320 				an2.setOpaque(false);
 321 			} else if (e.getSource() == user) {
 322 				if (a == 0) {
 323 			 lie1.setIcon(new javax.swing.ImageIcon("images/直线2.png"));
 324 				}
 325 			} else if (e.getSource() == pass) {
 326 				if (b == 0) {
 327 			 lie2.setIcon(new javax.swing.ImageIcon("images/直线2.png"));
 328 				}
 329 			} else if (e.getSource() == text3) {
 330 				text3.setForeground(new Color(170, 170, 170));
 331 			} else if (e.getSource() == text4) {
 332 				text4.setForeground(new Color(170, 170, 170));
 333 			} else if (e.getSource() == ma) {
 334 				ma.setIcon(new javax.swing.ImageIcon("images/二码.png"));
 335 			}
 336 		}
 337 	}

11.6 本章小结

本章主要讲解了Java中比较流行的GUI图形用户工具——Swing。首先讲解了Swing的顶级窗口,包括Jframe和Jdialog;其次讲解了布局管理器,包括FlowLayout、BorderLayout、GridLayout等;然后讲解了事件处理机制,以及常见的事件处理;最后讲解了Swing常用的组件,包括面板组件、文本组件、标签组件等。通过本章的学习,读者会了GUI的开发原理、开发技巧和开发思想,实现更高级的编程。

你可能感兴趣的:(Java,java,intellij-idea,swing)