跳至主要內容

[JAVA]-GUI

holic-x...大约 95 分钟JAVAGUI

[JAVA]-GUI

GUI学习TIP

​ 碎碎念:GUI相关的学习主要还是在尚未接触网页开发的萌新时刻,了解C#后一开始对这种图形化界面开发的内容还是蛮感兴趣的,但当时可参考的学习和组件扩展资源有限,后面也自己学着跌跌撞撞踩了不少坑,捣鼓了几个应用程序也算是小有成就。工作之后则主要接触web应用开发多点,因而对于这块的了解也渐渐弱化了,但偶然发现身边好多小伙伴并不了解甚至不知道这块的内容,秉承着”没有任何一件事物是无用论”的信念感👻,虽然在工作上这块内容的使用场景并不多,但基于此前了解和学习,我对于类似技术应用场景相关内容的理解会更加清晰,更能快速定位技术实现的核心思路,从而帮助我更好地去接收一项新的概念或者技术栈。(PS:如果不感兴趣的小伙伴可直接移步WEB应用开发相关的内容~)

1.AWT与Swing

【1】AWT

​ AWT 是Abstract Window ToolKit(抽象窗口工具包)的缩写,这个工具包提供了一套与本地图形界面进行交互的接口。AWT 中的图形函数与操作系统所提供的图形函数之间有着一一对应的关系,被称为peers。 也就是说,当利用 AWT 来构建图形用户界面的时候,实际上是在利用操作系统所提供的图形库。由于不同操作系统的图形库所提供的功能是不一样的,在一个平台上存在的功能在另外一个平台上则可能不存在。为了实现Java语言所宣称的"一次编译,到处运行"的概念,AWT 不得不通过牺牲功能来实现其平台无关性,也就是说,AWT 所提供的图形功能是各种通用型操作系统所提供的图形功能的交集,但是在不同的操作系统上展示的还是会有不同。由于AWT 是依靠本地方法来实现其功能的,我们通常把AWT控件称为重量级控件。

​ AWT是Sun不推荐使用的工具集。然而它在许多非桌面环境如移动或嵌入式设备中有着自己的优势:

  • 更少的内存:对运行在有限环境中的GUI程序的开发,是合适的。

  • 更少的启动事件:由于AWT组件是本地由操作系统实现的。绝大多数的二进制代码已经在如系统启动的时候被预装载了,这降低了它的启动事件。

  • 更好的响应:由于本地组件由操作系统渲染。

  • 成熟稳定的:能够正常工作并很少使你的程序崩溃。

然而事物具有两面性,AWT也有着许多缺点:

  • 更少组件类型:表和树这些重要的组件缺失了。它们是桌面应用程序中普遍使用的。

  • 缺乏丰富的组件特征:按钮不支持图片。

  • 无扩展性:AWT的组件是本地组件。JVM中的AWT类实例实际只是包含本地组件的引用。唯一的扩展点是AWT的Canvas组件,可以从零开始创建自定义组件。然而无法继承和重用一个已有的AWT组件。

【2】Swing

​ Swing 是在AWT的基础上构建的一套新的图形界面系统,它提供了AWT 所能够提供的所有功能,并且用纯粹的Java代码对AWT 的功能进行了大幅度的扩充。例如说并不是所有的操作系统都提供了对树形控件的支持, Swing 利用了AWT 中所提供的基本作图方法对树形控件进行模拟。由于 Swing 控件是用100%的Java代码来实现的,因此在一个平台上设计的树形控件可以在其他平台上使用。由于在Swing 中没有使用本地方法来实现图形功能,我们通常把Swing控件称为轻量级控件。

现在的swing在某些领域有着明显优势:

  • 丰富的组件类型:Swing提供了非常广泛的标准组件。这些组件和SWT一样丰富。基于它良好的可扩展性,除了标准组件,Swing还提供了大量的第三方组件。许多商业或开源的Swing组件库在开发多年后都已经可以方便地获取了。

  • 丰富的组件特性:Swing不仅包含了所有平台上的特性,它还支持根据程序所运行的平台来添加额外特性。Swing组件特性遵循特定原则,易于扩展,因此能够提供较SWT和AWT更多的功能。

  • 好的组件API模型支持:Swing遵循MVC模式,这是一种非常成功的设计模式。它的API成熟并设计良好。经过多年的演化,Swing组件APIs变得越来越强大,灵活和可扩展。它的API设计被认为是最成功的GUI API之一。较之SWT和AWT更面向对象,也更灵活而可扩展。

  • 标准的GUI库:Swing和AWT一样是JRE中的标准库。因此,你不用单独地将它们随你的应用程序一起分发。它们是平台无关的,不用担心平台兼容性。

  • 可扩展和灵活性。Swing完全由Java代码实现。Swing基于MVC的结构使得它可以发挥Java作为一门面向对象语言的优势。它提供了许总体上良好的性能。

​ 当然,swing也有着许多不足之处:比如swing比AWT更多的内存消耗。Swing自己实现了所有组件。因此,它在运行时装载了大量的类。而在运行时Java在堆上创建小的对象导致了额外的堆空间消耗。而许多小的对象难以有效地被垃圾回收机制回收。因此,Swing应用程序通常会因无法及时回收冗余的对象而导致性能下降。

要创建GUI程序,可以大概的分为以下几步:

a.创建顶层窗口
b.创建组件、如:文本框、标签、按钮
c.确定窗口中各组件的排列方式
d.将组件添加到窗口

【3】容器和组件的区别

容器(Container)是Component的子类,本身也是一个组件,具有组件的所有性

2.AWT容器与布局管理器

【1】AWT容器

​ 任何窗口都可被分解成一个空的容器,容器里盛装了大量的基本组件,通过设置这些基本组件的大小、位置等属性,就可以将该空的容器和基本组件组成一个整体的窗口。图形界面编程非常简单,类似于拼图游戏,容器类相当于“模版”,而普通组件则类似于拼图的图块。创建图形用户界面的过程就是完成拼图的过程。

​ 容器(Container)是Component的子类,因此容器对象本身也是一个组件,具有组件的所有性质,可以调用Component类的所有方法

Component类提供了如下几个常用方法来设置组件的大小、位置和可见性等

setLocation(int x,int y):设置组件的位置

setSize(int width;int height):设置组件的大小

setBounds(int x,int y,int width,int height):同时设置组件的位置、大小

setVisible(Boolean b):设置该组件是否可见

容器还可以盛装其他组件,容器类(Container)提供了如下几个常用方法来访问容器里的组件:

Component add(Component comp):向容器中添加其他组件,并返回被添加的组件

Component getComponent(int x,int y):返回指定点的组件

int getComponentCount():返回该容器组件内的数量

Component[] getComponent():返回该容器内的所有组件

AWT主要提供了如下两种主要的容器类型

Window:可独立存在的顶级窗口

Panel:可作为容器容纳其他组件,但不能独立存在,必须被添加到其他容器中(如:window、Panel或Applet等)

AWT容器之间的继承关系如下图所示:

🔖框架(Frame/JFrame)

Frame代表常见的窗体,它是Window类的子类,具有如下几个特点

  • Frame对象有标题,允许通过拖拉来改变窗口的位置、大小。

  • 初始化时不可见,可以使用setVisible(true)使其显示出来。

  • 默认使用BorderLayout作为其布局管理器。

public class MyFrame {
	public static void main(String[] args) {
		//1.创建窗体(指定名称)
		JFrame jf = new JFrame("第一个窗体学习");
		//2.设置窗体出现的位置(x,y)以及窗体的大小(weight,height)
		jf.setBounds(100, 100, 300, 400);
		//3.设置窗体的可见性
		jf.setVisible(true);
		//4.设置窗体的关闭方式
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		/**
		 * 5.其它操作:
		 * 设置窗体不能编辑:jf.enable(false);
		 * 设置窗体不能缩放:jf.setResizable(false);
		 */
	}
}

🔖面板(Panel)

​ Panel是AWT组件中的另外一个典型的容器,它代表不能独立存在、必须放在其他容器中的容器。Panel的外在表现为一个矩形区域,该区域内可盛装其他组件。Panel容器存在的意义在于为其他组件提供空间,Panel容器具有如下特点:

  • 可作为容器来盛装其他组件,为放置组件提供空间

  • 不能单独存在,必须放置到其他容器中

  • 默认使用FlowLayout作为其布局管理器。

public class JPanelTest {
	public static void main(String[] args) {
		JFrame jf = new JFrame("测试JPanel");
		//1.定义一个文本域和一个按钮
		JTextField jtf = new JTextField(10);
		JButton jb = new JButton("测试");
		//2.定义一个JPanel画板,再将文本域和文本框放置在画板中
		JPanel jp = new JPanel();//JPanel默认格局为FlowLayout
		jp.add(jtf);
		jp.add(jb);
		//3.将画板放置在JFrame窗体中
		jf.add(jp);
		//4.设置窗体的相关属性
		jf.setBounds(100, 100, 400, 300);
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
}

🔖ScrollPane

​ ScrollPane是一个带有滚动条的容器,它也不能独立存在,必须被添加到其他容器中。ScrollPane容器具有如下特点:

  • 可作为容器来盛装其他组件,当组件占用空间过大时,ScrollPane自动产生滚动条。当然也可以通过构造器参数来指定默认具有滚动条

  • 不能独立存在,必须放置在其他容器中

  • 默认使用BorderLayout作为其布局管理器。ScrollPane通常用于盛装其他容器,所以通常不允许改变ScrollPane的布局管理器。

public class JScrollPaneTest {
	public static void main(String[] args) {
		JFrame jf = new JFrame("测试JScrollPane");
		jf.setBounds(200, 200, 400, 300);
		//1.创建JScrollPane容器,并在创建的时候设置相关属性
		/**
		 * 第1个参数是要显示的位置,第2个参数是垂直方向上的滑动条是否显示,
		 * 第3个参数是方向上的滑动条是否显示
		 * JScrollPane j = new JScrollPane(view, vsbPolicy, hsbPolicy);
		 * 相应的均有always、needed、never三种分别对应总是显示、需要的时候显示、从不显示		 * 这三种情况,根据相应的要求进行选择
		 */
		//2.将文本框或者是文本域添加到JScrollPane中(用JScrollPane的构造方法实现)
		JTextField jtf = new JTextField();
		JButton jb = new JButton("测试");
		JScrollPane jsp = new JScrollPane(jtf,JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
		/**
		 * jsp.add(jtf);    jsp.add(jb);
		 * 上述方法将组件添加到JScrollPane中却无法显示
		 * 随后发现可以通过JScrollPane的构造方法将相应的组件添加到JScrollPane容器中
		 */
		//3.将JScrollPane容器添加到JFrame窗体中,并设置窗体的相关属性
		jf.add(jsp);
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
}

【2】AWT布局管理器

​ 为了使生成的图形用户界面具有良好外观与运行平台没有任何关系,Java提供了布局管理器这个工具类来管理组件在容器中的布局,而不使用直接设置组件位置和大小的方式。

​ 例如:Lable lable = new Lable(“Hello World”);如果让lable标签能够刚好容纳Hello World字符串,也就是实现该标签的最佳大小,即:在Windows平台中可能应该设置长100px,高20px;而在Unix系统上需要设置长120px;高24px;如果让程序员去手动控制每个组件的大小、位置,这将给编程带来巨大的困难,为了解决这个问题,Java提供了LayoutManager布局管理器类,LayoutManager可以根据运行平台来调整组件的大小,程序员要做的是就是为容器选择合适的布局管理器。

​ 所有的AWT容器都有默认的布局管理器,如果没有为容器指定布局管理器,则该容器使用默认的布局管理器。为容器指定布局管理器通过调用容器的setLayout(LayoutManager lm)方法来完成。

​ AWT提供了FlowLayout、BorderLayout、GridLayout、GridBagLayout、CardLayout5个常用的布局管理器,Swing还提供了一个BoxLayout布局管理器。

BorderLayout

​ BorderLayout(边界布局管理器)将容器分为EAST、SOUTH、WEST、NORTH、CENTER5个区域,普通组件可以被放置在这个5个区域的任意一个里面。

​ 改变BorderLayout的容器大小时,NORTH、SOUTH会在水平方向上调整,而EAST、WEST在垂直方向上调整,CENTER会根据调整在水平、垂直方向上都进行调整。使用BorderLayout布局管理器时需注意以下两点:

​ 向使用BorderLayout布局管理器的容器中添加组件时,需要指定要添加到哪个区域中。如果没有指定添加哪个区域中,则默认添加到中间区域中。

​ 如果向同一个区域中添加多个组件时,后放入的组件会覆盖先放入的组件。

Frame、Dialog、ScrollPane默认使用BorderLayout布局管理器,BorderLayout有如下两个构造器。

  • BorderLayout():使用默认的水平间距、垂直间距创建BorderLayout布局管理器。

  • BorderLayout(int hgap,int vgap):使用指定的水平间距、垂直间距创建BorderLayout布局管理器。

    使用BorderLayout布局管理器时,应该使用BorderLayout类的几个静态常量来指定组件添加到哪个区域内。对应的常量为:EAST、SOUTH、WEST、NORTH、CENTER。

public class BorderLayoutTest {
	/**
	 * BorderLayout布局管理器
	 * 默认将组件添加到中间的位置,有需要的话则自行指定要添加的位置
	 * 如果是要将多个组件添加到同一个区域中,后续的组件会对之前的内容进行覆盖
	 * 因此可以考虑将多个组件放入一个容器中(例如JPanel),然后再将该容器
	 * 放入到相应的位置
	 */
	public static void main(String[] args) {
		//1.创建JFrame(JFrame默认是BorderLayout布局)
		JFrame jf = new JFrame();
		jf.setBounds(100,100,400,300);
		//2.将多个按钮依次放入到相应东西南北中的位置
		jf.add(new JButton("东面"),BorderLayout.EAST);
		jf.add(new JButton("西面"),BorderLayout.WEST);
		jf.add(new JButton("南面"),BorderLayout.SOUTH);
		jf.add(new JButton("北面"),BorderLayout.NORTH);
		jf.add(new JButton("中面"));//默认是中间的位置,可以省略
		//jf.add(new JButton("中面"),BorderLayout.CENTER);
		/**
		 * 如果要将多个组件放入同一个区域,则将多个组件封装到一个容器中,然后再将这个容器
		 * 放入指定的区域(避免造成覆盖)
		 * 此处以两个按钮为例,将两个按钮放入JPanel(JPanel默认是FlowLayout布局)中,
		 * 随后将JPanel放到南面的位置,从而可以看到南面的按钮被覆盖了
		 */
		JPanel jp = new JPanel();
		jp.add(new JButton("南面1"));
		jp.add(new JButton("南面2"));
		jf.add(jp,BorderLayout.SOUTH);
		//3.设置窗体的相关属性
		jf.pack();
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
}

🔖FlowLayout

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

​ FlowLayout有3个常用的构造方法:

  • FlowLayout():使用默认的对齐方式及默认的垂直间距、水平间距创建FlowLayout布局管理器。

  • FlowLayout(int align):使用指定的对齐方式及默认的垂直间距、水平间距创建FlowLayout布局管理器。

  • FlowLayout(int align,int hgap,int vgap):使用指定的对齐方式以及指定的垂直间距、水平间距创建一个FlowLayout布局管理器。

​ 在Panel(面板)和Applet(Java小应用程序)中默认为使用FlowLayout布局管理器,Frame默认使用BorderLayout布局管理器,下面将使用FlowLayout布局管理器来管理Frame容器中的组件。

public class FlowLayoutTest {
	/**
	 * FlowLayout布局管理器
	 * JPanel、Applet默认是FlowLayout布局管理器:从左到右流水排列,遇到边界则换行
	 */
	public static void main(String[] args) {
		//1.创建JFrame窗体,设置相关属性
		JFrame jf = new JFrame();
		//2.创建JPanel画板,将20个按钮依次添加到指定的画板
		JPanel jp = new JPanel();
		for(int i=0;i<20;i++)
			jp.add(new JButton("按钮"+i));
		//3.将画板添加到窗体中,并设置窗体的相关属性
		jf.add(jp);
		jf.pack();//使得组件自动填充窗体
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
}

GridLayout

​ GridLayout(网格布局管理器)布局管理器将容器分割成纵横分隔的网格,每个网格所占的区域大小相同,当向使用GridLayout布局管理器的容器中添加组件时,默认为从左向右,从上向下依次添加到每个网格中,与FlowLayout不同的是,放置在GridLayout布局管理器中的各个组件的大小由组件所处的区域来决定。

​ GridLayout有如下两个构造器:

  • GridLayout(int rows,int cols):采用指定的行数与列数,以及默认的横向间距、纵向间距将容器分割成多个网格。

  • GridLayout(int rows,int cols,int hgap,int vgap):采用指定的行数、列数,以及指定的水平间距,垂直间距将容器分割成多个网格。

public class GridLayoutTest {
	/**
	 * GridLayout网格布局器
	 * 可以将容器分为纵横分割的网格,每个网格所占的区域大小相同
	 * 默认从左到右、从上到下依次添加每个组件
	 * 可以采用指定的行数、列数、水平间距、垂直间距进行创建
	 */
	public static void main(String[] args) {
		//创建一个简易的计算器面板
		JFrame jf = new JFrame("简易计算器面板设计");
		//1.创建文本框,用于接收数据输入
		JTextField jt = new JTextField();
		/**
		 * 2.创建基本的按钮面板,用以存放按钮组件
		 * 此处先将组件元素放置在一个数组中,随后再依次添加这些组件
		 */
		JPanel jp = new JPanel();
		jp.setLayout(new GridLayout(3,5,4,4));
		String[] s = {"1","2","3","4","5","6","7","8","9","0","+","-","*","/","."};
		for(int i=0;i<s.length;i++)
			jp.add(new JButton(" "+s[i]+" "));
		//3.将组件添加到窗体的相应位置
		jf.add(jt,BorderLayout.NORTH);
		jf.add(jp,BorderLayout.NORTH);
		jf.pack();
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
}

BoxLayout

​ BoxLayout(盒子布局管理器),它保留了GridBagLayout的很多优点,同时又比它使用简单。BoxLayout可以在垂直和水平两个方向上摆放GUI组件,BoxLayout提供了一个简单的构造器:

  • BoxLayout(Container c,int axis):创建一个基于指定容器c的盒子布局管理器,该布局管理器的组件按照axis(轴线)方向排列。其中axis有BoxLayout.X_AXIS(X轴)和BoxLayout.Y_AXIS(Y轴)两个方向。
public class BoxLayoutTest {
	/**
	 * BoxLayout格局适用于Frame,在JFrame使用中会出错
     * Frame中没有设置窗体的关闭方式,因此通过控制台进行关闭
	 */
		public static void main(String[] args) {
			Frame jf=new Frame("第一个窗口");
			//设置窗体的初始坐标位置  以及宽度和高度
			jf.setBounds(50, 50, 500, 300);
			BoxLayout bl =new BoxLayout(jf, BoxLayout.Y_AXIS);
			jf.setLayout(bl);
			jf.add(new JButton("按钮1"));
			jf.add(new JButton("按钮2"));
			//设置窗体可见性
			jf.setVisible(true);
	}
}
public class BoxLayoutTest2 {
	public static void main(String[] args) {
		Frame frame =new Frame("测试");
		//定义水平摆放的组件Box对象
		Box hor = Box.createHorizontalBox();
		//定义垂直摆放box对象
		Box  ver =Box.createVerticalBox();
		hor.add(new JButton("水平按钮1"));
		hor.add(Box.createHorizontalGlue());
		hor.add(new JButton("水平按钮2"));
		hor.add(Box.createHorizontalStrut(10));
		hor.add(new JButton("水平按钮3"));	
		ver.add(new JButton("垂直按钮1"));
		hor.add(Box.createVerticalGlue());
		ver.add(new JButton("垂直按钮2"));
		hor.add(Box.createVerticalStrut(10));
		ver.add(new JButton("垂直按钮3"));
		frame.add(hor, BorderLayout.NORTH);
		frame.add(ver);
		frame.pack();
		frame.setVisible(true);
	}
}

绝对定位

Java也可以对GUI组件进行绝对定位,在Java容器中采用绝对定位的步骤如下:

  • 将Container的布局管理器设置为null:container.setLayout(null)

  • 向容器中添加组件时,先调用setBounds()或setSize()方法来设置组件的大小、位置,或者直接创建GUI组件时通过构造参数指定该组件的大小、位置,然后将该组件添加到容器中

public class AbsoluteLayoutTest {
	public static void main(String[] args) {
		/**
		 * AbsoluteLayout绝对定位
		 * 1.将指定容器的默认布局置为null,container.setLayout(null);
		 * 2.根据自定的坐标位置设置组件的位置、大小,将组件添加到容器中
		 */
		//1.将指定容器的默认布局置为null
		JFrame jf = new JFrame();
		jf.setBounds(50, 50, 500, 300);
		jf.setLayout(null);
		//2.对每个组件设置相应的属性(出现位置、大小等)
		JButton jb = new JButton("按钮1");
		jb.setBounds(100, 200, 40, 40);
		JTextField jt = new JTextField();
		jt.setBounds(100, 300, 40, 40);
		//3.直接将相应的组件放入容器中
		jf.add(jb);
		jf.add(jt);
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
}

3.常用组件

​ AWT组件需要调用运行平台的图形界面来创建和平台一致的对等体,因此AWT只能使用所有平台都支持的公共组件,所以AWT只提供了一些常用的GUI组件。

【1】基本组件

AWT提供的基本组件:

Button:按钮,可以接收单击事件

CheckBox:复选框组件(也可变成单选框组件)

CheckBoxGroup:用于将多个CheckBox组件组合成一组,一组Checkbox组件只有一个可以被选中,即全部变成单选框组件。

Choice:下拉选择框组件

Frame:窗口,在GUI程序里通过该类创建一个窗口。

Label:标签类,用于放置提示性文本

List:列表框组件,可以添加多项条目

Panel:不能单独存在的基本容器类,必须放到其他容器中去

Scrollbar:滑动条组件,如果需要用户输入位于某个范围的值,可以使用滑动条组件。当创建一个滑动条时,必须指定它的方向,初始值、滑块的大小、最小值和最大值

ScrollPane:带水平及垂直滚动条的容器组件。

TextArea:多行文本域

TextField:单行文本框

Button(按钮)

常用的构造方法:

Button():创建一个按钮,按钮上的标签没有任何内容

Button(String LableContent):创建一个按钮,自定义按钮标签上的内容

常用的方法:

setBackground(Color color):设置按钮的背景颜色

setEnable(boolean b):设置按钮是否可用

setFont(Font f):设置按钮标签的字体

setForeground(Color color):设置按钮的前景色

setLabel(String text):设置按钮标签的内容

setVisible(boolean b):设置按钮是否可见

setFocusPainted(false):去除焦点(点击文本时显示的边框)

setBorderPainted(false):去除按钮边框

public class ButtonTest {
	public static void main(String[] args) {
		JFrame jf = new JFrame("按钮测试...");
		jf.setBounds(100, 200, 400, 300);
		//1.创建一个按钮,定义相应的按钮名称
		JButton bt = new JButton("按钮1");
		//2.设置按钮的背景颜色(此处设置为黑色)
		bt.setBackground(Color.black);
		//3.设置按钮标签的字体
		Font font =new Font("华文行楷", Font.PLAIN, 15);
		bt.setFont(font);
		//4.设置按钮的前景色(此处设置为红色)
		bt.setForeground(Color.red);
		//5.设置标签内的内容(此处修改为按钮)
		bt.setLabel("按钮");
		//6.设置按钮是否可用(可否点击)
		bt.setEnabled(true);
		//7.设置按钮是否可见
		bt.setVisible(true);
		jf.add(bt);
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
}

🔖Label(标签)

常用的构造方法:

Label():创建一个标签,标签上没有任何文字

Label(String text):创建一个标签,并且自定义标签上的文字。

Label(String text,int alignment):创建一个标签,并且自定义标签上的文字及对齐方式。

常用方法:

setAlignment(int alignment):设置标签文本的对齐方式

setBackground(Color color):设置标签的背景色

setEnable(boolean b):设置标签是否可用

setFont(Font font):设置标签文本的字体

setForeground(Color color):设置标签的前景色

setText(String text):设置标签的内容

setVisible(boolean b):设置标签是否可见

setHorizontalAlignment(SwingConstants.属性名(对齐方式)):设置文本对齐方式

public class LabelTest {
	public static void main(String[] args) {
		JFrame jf = new JFrame("Label标签测试...");
		//1.用Label的三个构造方法分别定义三个标签
		Label b1 = new Label();
		Label b2 = new Label("标签2");
		Label b3 = new Label("标签3",Label.LEFT);
		//2.设置标签文本的对其方式(此处设置标签2居左对其)
		b2.setAlignment(Label.LEFT);
		//3.设置标签的背景色(此处设置标签2的背景色为蓝色)
		b2.setBackground(Color.BLUE);
		//4.设置标签文本的字体(此处设置标签2的文本字体)
		Font font =new Font("华文行楷", Font.PLAIN, 15);
		b2.setFont(font);
		//5.设置标签的内容(此处设置标签1的内容为标签1)
		b1.setText("标签1");
		//6.设置标签是否可见(此处隐藏标签3的内容)
		b3.setVisible(false);
		JPanel jp = new JPanel();
		jp.add(b1);
		jp.add(b2);
		jp.add(b3);
		jf.add(jp);
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
}

🔖TextField(文本框)

常用的构造方法:

TextField():创建一个文本框

TextField(String text):创建一文本框,并且有初始值

TextField(String text,int columns):创建一个文本框,有初始值,并且设置列数

TextField(int columns):创建一个文本框,没有初始内容,可设置列数。

常用方法:

setBackground(Color color):设置文本框的背景色

setColumns(int columns):设置文本框的列数

setEditable(boolean b):设置文本框是否可编辑

setEnable(boolean b):设置文本框是否可用

setFont(Font f):设置文本框文字的字体

setForeground(Color color):设置文本框的前景色

setText(String text):设置文本框的文本内容

setVisible(boolean b):设置文本框是否可见

setBorder(null):去除文本框的边框线

setBackground(NULL):使得文本框背景颜色与框架颜色一致

setOpaque(false):使得文本框设为透明

public class TextFieldTest {
	public static void main(String[] args) {
		JFrame jf = new JFrame("文本框测试...");
		//1.结合TextField的四个构造方法创建四个文本框
		JTextField jt1 = new JTextField();
		JTextField jt2 = new JTextField("文本框2");
		JTextField jt3 = new JTextField("文本框3",JTextField.RIGHT);
		JTextField jt4 = new JTextField(10);
		//2.设置文本框的背景色(此处设置文本框2的背景色为绿色)
		jt2.setBackground(Color.green);
		//3.设置文本框的列数(此处设置文本框2的列数为20)
		jt2.setColumns(20);
		//4.设置文本框是否可进行编辑(此处设置文本框3的内容不可进行编辑)
		jt3.setEditable(false);
		//5.设置文本框的内容是否可用(此处设置文本框2的内容不可用)
		jt2.setEnabled(false);
		//6.设置文本框的字体(此处设置文本框3的字体)
		Font font =new Font("华文行楷", Font.PLAIN, 15);
		jt3.setFont(font);
		//7.设置文本框的前景色(此处设置文本框2的前景色为红色)
		jt2.setForeground(Color.red);
		//8.设置文本框的内容(此处设置文本框1的内容)
		jt1.setText("文本框1");
		//9.设置文本框是否可见(此处隐藏文本框4)
		jt4.setVisible(false);
		JPanel jp = new JPanel();
		jp.add(jt1);
		jp.add(jt2);
		jp.add(jt3);
		jp.add(jt4);
		jf.add(jp);
		jf.pack();
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
}

TextArea(多行文本域/文本区)

常用的构造方法

TextArea():创建一个默认大小的空文本区

TextArea(String text):创建一个默认大小的文本区,并初始其内容

TextArea(String text,int rows,int columns):创建一个含有初始内容的文本区域,并设定文本区域的行数跟列数。

TextArea(String text,int rows,int columns,int scrollbars):创建一个文本区,并且自定义文本内容,设定文本区域的内容,设定文本区的行数跟列数,设置滚动条的状态

TextArea(int rows,int columns):创建一个文本区,并且自定义文本区的行数跟列数。

常用的方法:

append(String text):在文本结尾追加自定义的文本。

insert(String text,int begin):在指定的位置插入自定义文本

replaceRange(String text,int begin,int end):将指定范围内的文本替换为自定义的文本

setRows(int rows):设置文本的行数

setColumns(int columns):设置文本的列数

public class TextAreaTest {
	public static void main(String[] args) {
		JFrame jf = new JFrame("文本域测试...");
		//1.通过提供的五个文本域的构造方法创建五个文本域
		JTextArea jt1 = new JTextArea();
		JTextArea jt2 = new JTextArea("文本域2");
		JTextArea jt3 = new JTextArea("10*10的文本域3",10,10);
		TextArea jt4 = new TextArea("5*5的文本域4",5,5,TextArea.SCROLLBARS_BOTH);
		JTextArea jt5 = new JTextArea(10,10);
		//2.在文本域的结尾追加内容(此处在文本域2的末尾追加内容)
		jt2.append("没有设置指定的行数和列数");
		//3.在指定的位置插入自定义文本(此处在文本域3的前方插入自定义文本)
		jt3.insert("插入了内容---",0);
		//4.将指定范围的文本内容替换为自定义内容(此处将文本域4的内容进行替换)
		jt4.replaceText("haha", 0, 3);
		//5.设置文本域的行数和列数(此处设置文本域1的行数列数分别为5*10)
		jt1.setRows(5);
		jt1.setColumns(10);
		jf.setLayout(new GridLayout(5,1,5,5));
		jf.add(jt1);
		jf.add(jt2);
		jf.add(jt3);
		jf.add(jt3);
		jf.add(jt4);
		jf.add(jt5);
		jf.pack();
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
}

Checkbox(复选框/单选按钮)

常用的构造方法:

Checkbox():创建一个复选框

Checkbox(String text):创建一个复选框,并自定义标签

Checkbox(String text,CheckboxGroup group,boolean state):创建一个复选框,并将其加入到一个复选框组中,设置复选框是否是被选中,加入到复选框组中的复选框将变成单选。

Checkbox(String text,boolean state):创建一个复选框,并设置其是否被选中

常用的方法:

setCheckboxGroup(CheckboxGroup group):将一个复选框加入大一个组中去,将其变为一个单选

setLabel(String label):为复选框添加一个文本标签

setState(boolean b):设置浮选框的状态,是否被选中。

public class CheckBoxTest extends JFrame{
	//1.定义单选按钮(将CheckBox按钮放置在一个group中进行关联)
	CheckboxGroup  group = new CheckboxGroup();
	Checkbox male = new Checkbox("男", group, false);
	Checkbox female = new Checkbox("女", group, true);//初始化默认选择女
	//2.定义复选按钮
	Checkbox music = new Checkbox("音乐",false);
	Checkbox sport = new Checkbox("体育",false);
	Checkbox sleep = new Checkbox("睡觉",false);
	Checkbox qdm = new Checkbox("敲代码",false);
	//初始化窗体,将按钮加载到窗体中
	public CheckBoxTest()
	{
		this.add(male);
		this.add(female);
		this.add(music);
		this.add(sport);
		this.add(sleep);
		this.add(qdm);
		qdm.setState(true);//默认选择敲代码选项
		this.setLayout(new GridLayout(6,1,0,0));
		this.setSize(600, 400);
		this.setVisible(true);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	public static void main(String[] args) {
		//测试
		new CheckBoxTest();
	}
}

List(列表框)

常用的构造函数:

List():创建一个空的列表框

List(int rows):创建一个空的列表框,并设置其行数

List(int rows,boolean multipleModal):创建一个空的列表框并设置其是否使用多选模式

常用的方法:

add(String item):为列表框追加项目

add(String item,int index):在列表框的index位置添加项目,如果此位置上已有项目,则已有的项目以及项目后面的内容相应的向后移动一个位置。

clear():清除列表框的所有项目

getItemCount():返回列表框的项目总数

remove(int index):在列表框的index位置处,删除项目

select(int index):选中列表框index位置上的项目

deselect(int index):排除已选中项目的index位置上的项目

getSelectedItem():返回String值,返回一个选中的项目,如果选中多个就会返回null

getSelectedItems():返回String[]数组,返回所有被选中的项目

removeAll():清除列表框的所有项目

setMultipleMode(boolean b):设置是否采用多行选择模式

Choice(下拉框)

常用的构造方法:

Choice():创建一个下拉框

常用的方法:

addItem(String item):为选择框添加一个项目

getItem(int index):返回下拉框中index位置上的项目的文本标签

getItemCount():返回下拉框中所有的项目总数。

getSelectedItem():返回以选中的项目的文本标签

insert(String item,int index):在index位置插入文本标签为item的项目

remove(int index):删除index位置上的项目

removeAll():删除所有项目

select(int index):选中index位置上的项目

public class ListTest extends JFrame{
	//1.创建一个文本框长度为10的列表选项,并设置是否使用多选模式
	List list = new List(10,true);
	//2.创建下拉框选项
	Choice choice = new Choice();
	//3.初始化窗体
	public ListTest()
	{
		//设置布局管理器
		this.setLayout(new GridLayout(2,1,0,0));
		//将相应的组件添加到列表、下拉框选项中
		this.list.add("水果");
		this.list.add("咖啡");
		this.list.add("巧克力");
		this.choice.add("本科");
		this.choice.add("硕士");
		this.choice.add("博士");
		//将列表和下拉框放置在窗体中
		this.add(list);
		this.add(choice);
		//设置窗体的相关属性
		this.setSize(600, 400);
		this.setVisible(true);
		this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	public static void main(String[] args) {
		//测试
		new ListTest();
	}
}
public class ComponentsTest {
	JFrame jf = new JFrame("练习");
	//1.定义相关的组件
	//定义文本域及相应的滑动框
	JTextArea jta = new JTextArea(3,8);
	JScrollPane jsp = new JScrollPane(jta, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
	//定义列表
	List list = new List(6,true);
	//定义下拉框
	Choice choice = new Choice();
	//定义单选按钮以及复选按钮(单选中男为默认选项)
	CheckboxGroup group = new CheckboxGroup();
	Checkbox nan = new Checkbox("男",group,true);
	Checkbox nv = new Checkbox("女",group,false);
	Checkbox isMarried = new Checkbox("是否结婚");
	//定义文本框
	JTextField jt = new JTextField(20);
	//定义按钮
	JButton bt = new JButton("OK");
	//2.设置组件的相关布局,并设置窗体的相关属性
	public void init()
	{
		//先将下拉框及列表中的项目添加进去
		list.add("橙色");
		list.add("黑色");
		list.add("黄色");
		choice.add("红色");
		choice.add("白色");
		choice.add("蓝色");
		//随后设置组件布局,先设置左侧上方的部分
		JPanel left = new JPanel();
		left.setLayout(new GridLayout(2,1,1,1));
		left.add(jsp);
		JPanel jp1 = new JPanel();
		jp1.add(choice);
		jp1.add(nan);
		jp1.add(nv);
		jp1.add(isMarried);
		left.add(jp1);
		//设置右侧上方的部分
		JPanel right = new JPanel();
		right.add(list);
		//设置下方的部分
		JPanel down = new JPanel();
		down.setLayout(new FlowLayout(FlowLayout.CENTER));
		down.add(jt);
		down.add(bt);
		//最后将所有的组件拼装到窗体中
		jf.add(left,BorderLayout.WEST);
		jf.add(right,BorderLayout.EAST);
		jf.add(down,BorderLayout.SOUTH);
		jf.pack();
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	public static void main(String[] args) {
		new ComponentsTest().init();
	}
}

对话框

Dialog

​ Dialog是Windows类的子类,是一个容器类,属于特殊组件。对话框是可以独立存在的顶级窗口,用法与普通窗口没什么区别。使用的是BorderLayout布局,使用对话框时需注意以下两点:

​ 对话框通常依赖于其他窗口,即含有一个父窗口(parent)

​ 对话框有非模式(non-modal)和模式(modal)两种,当某个模式对话框被打开之后,该模式对话框总是位于parent之上;模式对话框被关闭之前,它的依赖窗口无法获得焦点。

​ 有模式是在打开本对话框的时候不能使本应用的其他界面获取焦点

​ 无模式是在打开本对话框的时候可以使本应用的其他界面获取焦点

Dialog对话框有多个重载的构造器,参数如下:

owner:指定该对话框所依赖的窗口,即可以是窗口,也可以是对话框。

title:指定该对话框的窗口标题

modal:指定该对话框是否是模式的,是Boolean值,true表示模式,false表示非模式。

public class DialogTest {
	/**
	 * 对话框:分为模式对话框和非模式对话框
	 */
	//1.分别创建两个对话框:模式对话框和非模式对话框
	public static JFrame jf = new JFrame("对话框测试...");
	public static Dialog d1 = new Dialog(jf,"模式对话框",true);
	public static Dialog d2 = new Dialog(jf,"非模式对话框",true);
	//2.分别创建两个按钮以及一个文本框,用以区分两个对话框
	public static JTextField jtf = new JTextField();
	public static JButton bt1 = new JButton("模式对话框");
	public static JButton bt2 = new JButton("非模式对话框");
	//3.初始化相关内容,将组件添加到窗体中,并实现相应按钮事件的监听
	public static void init()
	{
		//为按钮设置监听事件
		bt1.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				d1.setVisible(true);
				jtf.setText("模式对话框打开...");
			}
		});
		bt2.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				d1.setVisible(true);
				jtf.setText("非模式对话框打开...");
			}
		});
		//将组件添加到窗体中
		jf.add(jtf, BorderLayout.NORTH);
		jf.add(bt1,BorderLayout.EAST);
		jf.add(bt2,BorderLayout.WEST);
		//设置窗体大小和可见性
		jf.setBounds(100, 100, 500, 500);
		jf.setVisible(true);
		jf.pack();
		//设置窗体关闭方式
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	public static void main(String[] args) {
		new DialogTest().init();
	}
}
FileDialog

​ FileDialog是一个文件对话框,它是Dialog的子类,用于打开或者保存文件。FileDialog提供了几个构造器,分别支持parent、title、mode三个构造参数,其中parent、title指定文件对话框的所属父窗口和标题;mode指定该窗口用于打开文件或保存文件,此参数支持两个值:FileDialog.LOAD、FileDialog.SAVE。

​ FileDialog提供了两个方法来获取被打开/保存文件的路径

getDirectory():获取FileDialog被打开/保存文件的绝对路径

getFile():获取FileDialog被打开/保存文件的文件名

public class FileDialogTest {
	/**
	 * 文本对话框:
	 * 用以打开文件
	 * 用以保存文件
	 */
	//1.分别创建两个文本对话框,一个用以打开文件,一个用以保存文件
	public static JFrame jf = new JFrame("文本对话框测试...");
	public static FileDialog fd1 = new FileDialog(jf,"打开文件",FileDialog.LOAD);
	public static FileDialog fd2 = new FileDialog(jf,"保存文件",FileDialog.SAVE);
	//2.分别创建两个按钮用以监听文本对话框
	public static JButton bt1 = new JButton("打开文件...");
	public static JButton bt2 = new JButton("保存文件...");
	//3.初始化窗体,并未两个按钮添加监听事件
	public static void init()
	{
		bt1.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				fd1.setVisible(true);
				System.out.println("打开文件信息:"+fd1.getDirectory()+"--"+fd1.getFile());
			}
		});
		bt2.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				fd2.setVisible(true);
				System.out.println("保存文件信息:"+fd2.getFile());
			}
		});
		//将相应的组件添加至窗体,并设置窗体属性
		jf.setBounds(100, 100, 300, 400);
		jf.add(bt1,BorderLayout.EAST);
		jf.add(bt2, BorderLayout.WEST);
		//设置窗体可见性
		jf.setVisible(true);
		jf.pack();
		//设置窗体关闭方式
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	public static void main(String[] args) {
		new FileDialogTest().init();
	}
}

参考示例

public class CommonTest {
	//1.定义相应的组件
	private JLabel jl1 = new JLabel("用户名:",JLabel.RIGHT);
	private JLabel jl2 = new JLabel("密码:",JLabel.RIGHT);
	private JLabel jl3 = new JLabel("学历:",JLabel.RIGHT);
	private JLabel jl4 = new JLabel("性别:",JLabel.RIGHT);
	private JLabel jl5 = new JLabel("爱好:",JLabel.RIGHT);
	private JLabel jl6 = new JLabel("选修课:",JLabel.RIGHT);
	private JLabel jl7 = new JLabel("备注:",JLabel.RIGHT);
	private JTextField jt1 = new JTextField(20);
	private JTextField jt2 = new JTextField(20);
	private Choice choice = new Choice();
	private CheckboxGroup group = new CheckboxGroup();
	private Checkbox nan = new Checkbox("男",group,true);
	private Checkbox nv = new Checkbox("女",group,false);
	private Checkbox football = new Checkbox("足球");
	private Checkbox basketball = new Checkbox("篮球");
	private Checkbox volleyball = new Checkbox("排球");
	private List list1 = new List(5,true);
	private JButton bt1 = new JButton(">>");
	private JButton bt2 = new JButton("<<");
	private List list2 = new List(5,true);
	private JTextArea jta = new JTextArea(5,30);
	JScrollPane jsp = new JScrollPane(jta,JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
	private JButton jb1 = new JButton("提交");
	private JButton jb2 = new JButton("重置");
	//2.设置组件布局,初始化相关组件
	public void init()
	{
		//为下拉框和列表添加选项
		list1.add("语文");
		list1.add("数学");
		list1.add("英语");
		choice.add("小学");
		choice.add("初中");
		choice.add("高中");
		choice.add("本科");
		choice.add("硕士");
		choice.add("博士");
		//上面5行作为一部分,用FlowLayout结合GridLayout实现
		/**
		 * 如果是让JLable标签的文本对齐方式为居右,可以在创建的JLabel标签
		 * 的时候直接进行指定,亦可以通过JLabel提供的方法实现设置文本对齐的
		 * 方式-->setHorizontalAlignment
		 * 例如jl.setHorizontalAlignment(SwingConstants.RIGHT);
		 * 可能出现的问题:虽然标签的文本设置为右对齐,但结果显示还是有所不同,是由
		 * 于在设定两个一组并添加到相应的画板的时候是通过FlowLayout.LEFT方式
		 * 添加,从而显示结果参差不齐,因此考虑将上5行内容分别分为左右两侧进行添加
		 * (对应各自的GridLayout布局,设定相应的间距),以此类推,从而得到相对
		 * 美观的布局极端的方法则是两个一组依次添加即可(上5,中2,下1)
		 */
		//先定义上半部分(前5行的内容)
		JPanel top = new JPanel();
		JPanel topLeft = new JPanel();
		//上半部分又分为左右两侧对应各自的GridLayout格局及其间距
		topLeft.setLayout(new GridLayout(5,1,25,25));
		topLeft.add(jl1);
		topLeft.add(jl2);
		topLeft.add(jl3);
		topLeft.add(jl4);
		topLeft.add(jl5);
		JPanel topRight = new JPanel();
		topRight.setLayout(new GridLayout(5,1,10,10));
		topRight.add(jt1);
		topRight.add(jt2);
		topRight.add(choice);
		JPanel jp1 = new JPanel();
		jp1.setLayout(new FlowLayout(FlowLayout.LEFT));
		jp1.add(nan);
		jp1.add(nv);
		topRight.add(jp1);
		JPanel jp2 = new JPanel();
		jp2.setLayout(new FlowLayout(FlowLayout.LEFT));
		jp2.add(football);
		jp2.add(basketball);
		jp2.add(volleyball);
		topRight.add(jp2);
		top.setLayout(new FlowLayout(FlowLayout.LEFT));
		top.add(topLeft);
		top.add(topRight);
		//随后将第6、7行作为中间部分,以此类推分为两侧并设定相应的间距
		JPanel center = new JPanel();
		JPanel centerLtft = new JPanel();
		centerLtft.setLayout(new GridLayout(2,1,125,125));
		centerLtft.add(jl6);
		centerLtft.add(jl7);
		JPanel jp3 = new JPanel(new FlowLayout(FlowLayout.LEFT));
		JPanel btj = new JPanel(new GridLayout(2,1,25,25));
		btj.add(bt1);
		btj.add(bt2);
		jp3.add(list1);
		jp3.add(btj);
		jp3.add(list2);
		JPanel centerRight = new JPanel(new GridLayout(2,1,5,5));
		centerRight.add(jp3);
		centerRight.add(jsp);//添加的是相应文本域的滚动条
		center.setLayout(new FlowLayout(FlowLayout.LEFT));
		center.add(centerLtft);
		center.add(centerRight);
		//最后定义第八行两个按钮作为底部部分
		JPanel down = new JPanel();
		down.add(jb1);
		down.add(jb2);
		//将所有的组件最终整合到窗体中,并设置窗体的相关属性
		JFrame jf = new JFrame("练习测试...");
		jf.add(top,BorderLayout.NORTH);
		jf.add(center, BorderLayout.CENTER);
		jf.add(down, BorderLayout.SOUTH);
		jf.pack();
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	public static void main(String[] args) {
		//测试
		new CommonTest().init();
	}
}

4.事件处理

【1】Java事件模型的流程

​ 为了使图形界面能够接受用户的操作,必须给各个组件加上事件处理机制。在事件处理的过程中,主要涉及3类对象。

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

Event(事件):事件封装了GUI组件上发生的特定事情,即:用户的一次操作。如果需要获得GUI组件上发生的事件的相关信息,都必须通过Event对象来获得。

Event Listener(事件监听器):负责监听事件源所发生的事件,并对各种事件做出响应处理。

​ 事件由AWT封装成相应的Event对象,该事件会触发事件源上注册的的事件监听器,事件监听器调用对应的事件处理器(事件监听器的实例方法)来做出相应的响应。

public class ButtonListenerTest {
	//按钮监听事件测试...
	public static JFrame jf = new JFrame("按钮单击监听测试...");
	public static JButton bt = new JButton("单击");
	public static JTextField jtf = new JTextField("hello!");
	public static void init()
	{
		//为按钮点击添加监听事件
		bt.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				System.out.println("按钮被点击了...");
				jtf.setText(jtf.getText()+"hi");
			}
		});
		//将组件添加到窗体中,并设置窗体的相关属性
		jf.add(bt,BorderLayout.SOUTH);
		jf.add(jtf,BorderLayout.NORTH);
		jf.setVisible(true);
		jf.pack();
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	public static void main(String[] args) {
		new ButtonListenerTest().init();
	}
}

【2】事件和事件监听器

​ 外部动作在AWT上进行操作时,系统会自动生成事件对象,这个事件对象就是EventObject子类的实例,该事件对象会触发注册到事件源(组件)上的监听器。

​ AWT事件机制涉及3个成员:事件源(组件)、事件(EventObject子类对象)和事件监听器(Listener),其中事件源(组件)直接通过new来创建一个AWT组件即可;事件是系统自动产生的,无须程序员来关心。监听器是整个事件处理的核心。

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

​ AWT事件分为两大类:低级事件和高级事件。

低级事件:低级事件是指基于特定动作的事件

ComponentEvent:组件事件,当组件尺寸发生变化、位置发生移动、显示/隐藏状态发生改变触发该事件

ContainerEvent:容器事件,当容器里发生添加组件、删除组件时触发该事件

WindowEvent:窗口事件,当窗口状态发生改变(打开、关闭、最大化、最小化)时触发该事件

FocusEvent:焦点事件,当组件得到焦点或失去焦点时触发该事件

KeyEvent:键盘事件,当按键被按下、松开、单击时触发该事件

MouseEvent:鼠标事件,当进行单击、按下、松开、移动鼠标等动作时触发该事件

高级事件:高级事件是基于语义的事件,它可以不和特定的动作相关联,而依赖于触发此事件的组件类

ActionEvent:动作事件,当按钮、菜单项被单击,在TextField中按Enter键是触发该事件

AdjustmentEvent:调节事件,在滑动条上移动滑块以调节数值时,触发该事件

ItemEvent:选项事件,当用于选中某项、或取消选中某项时触发该事件

TextEvent:文本事件,当文本框、文本域里的文本发生改变时触发该事件

AWT事件继承层次关系图

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

事件、监听器接口和处理器之间的对应关系

事件监听器接口处理器及触发时机
ActionEventActionListeneractionPerformed:按钮、文本框、菜单项被单击时触发
AdjustmentEventAdjustmentListeneradjustmentValueChanged:滑块位置发生改变时触发
ContainerEventContainerListenercomponentAdded:向容器中添加组件时触发
componentRemoved:从容器中删除组件时触发
FocusEventFocusListenerfocusGainer:组件得到焦点时触发
focusLost:组件失去焦点时触发
ComponentEventComponentListenercomponentHidden:组件被隐藏时触发
componentMoved:组件位置发生改变时触发
componentResized:组件大小发生改变时触发
componentShown:组件被显示时触发
KeyEventKeyListenerkeyPressed:按下某个按键时触发
keyReleased:松开某个按键时触发
keyTyped:单击某个按键时触发
MouseEventMouseListenermouseClicked:在某个组件上单击鼠标键时触发
mouseEntered:鼠标进入某个组件时触发
mouseExited:鼠标离开某个组件时触发
mousePressed:在某个组件上按下鼠标键时触发
mouseReleased:在某个组件上松开鼠标键时触发
MouseMotionListenermouseDragged:在某个组件上移动鼠标,且按下鼠标键时触发
mouseMoved:在某个组件上移动鼠标,且没有按下鼠标键时触发
TextEventTextListenertextValueChanged:文本组件里的文本发生改变时触发
ItemEventItemListeneritemStateChanged:某项被选中或取消选中时触发
WindowEventWindowListenerwindowActivated:窗口被激活时触发
windowClosed:窗口调用dispose()即关闭时触发
windowClosing:用户单击窗口右上角的“X”按钮时触发
windowDeactivated:窗口失去激活时触发
windowDeiconified:窗口被恢复时触发
windowIconified:窗口最小化时触发
windowOpened:窗口首次被打开时触发

鼠标监听事件

public class MourseListenerTest {
	/**
	 * 鼠标监听器有三个主要的内容
	 * 1.MouseListener:用于监听鼠标的进入、按下、单击、释放、退出
	 * 2.MouseMotionListener:用于监听鼠标的移动、拖动
	 * 3.MouseWheelListener:用于鼠标滑轮的监听
	 */
	//定义按钮用以测试相关的鼠标触发的事件
	public static JFrame jf = new JFrame("鼠标监听测试...");
	public static JButton bt = new JButton("按钮测试");
	public static void init()
	{
		//一个组件可以添加多个监听
		bt.addMouseListener(new MouseListener() {
			@Override
			public void mouseReleased(MouseEvent e) {
				System.out.println("鼠标释放...");
			}
			@Override
			public void mousePressed(MouseEvent e) {
				System.out.println("鼠标按下...");
			}
			@Override
			public void mouseExited(MouseEvent e) {
				System.out.println("鼠标退出...");
			}
			@Override
			public void mouseEntered(MouseEvent e) {
				System.out.println("鼠标进入...");
			}
			@Override
			public void mouseClicked(MouseEvent e) {
				System.out.println("鼠标单击...");
				/**
				 * 可以获取鼠标点击的相关信息,例如鼠标连续点击次数、
				 * 鼠标点击了什么键等相关信息,从而执行不同的操作
				 */
				//获取鼠标连续点击的次数
				int count = e.getClickCount();
				System.out.println("鼠标连续点击了"+count+"次");
				/**
				 * 获取用户的点击信息(点击了哪个键),用getButton方法实现
				 * 其返回值为整型数据
				 * 如果返回值为1,说明鼠标点击了左键
				 * 如果返回值为2,说明鼠标点击了滑轮键
				 * 如果返回值为3,说明鼠标点击了右键
				 * MouseEvent类提供了相应的常量对数据进行封装
				 * MouseEvent.BUTTON1,对应鼠标左键
				 * MouseEvent.BUTTON2,对应鼠标滑轮键
				 * MouseEvent.BUTTON3,对应鼠标右键
				 */
				int i = e.getButton();
				if(e.getButton()==MouseEvent.BUTTON1)
					System.out.println("用户点击了左键...");
				else if(e.getButton()==MouseEvent.BUTTON2)
					System.out.println("用户点击了滑轮键...");
				else if(e.getButton()==MouseEvent.BUTTON3)
					System.out.println("用户点击了右键...");
			}
		});
		bt.addMouseMotionListener(new MouseMotionListener() {
			@Override
			public void mouseMoved(MouseEvent e) {
				System.out.println("(没有点击的时候)鼠标移动...");
				//可以获取鼠标移动的相对位置
				System.out.println(e.getX());
				System.out.println(e.getY());
			}
			@Override
			public void mouseDragged(MouseEvent e) {
				System.out.println("(鼠标点击之后)鼠标拖动");
			}
		});
		bt.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				System.out.println("鼠标滑轮滚动...");
			}
		});
		//将按钮放置在窗体中进行测试,设置窗体的相关属性
		jf.add(bt);
		jf.pack();
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	public static void main(String[] args) {
		new MourseListenerTest().init();
	}
}

适配器模式

​ 如果需要实现某些监听 可以把多个接口定义用一个类来实现,然后在使用监听时,可以使用自定义的类,根据需求重写某些方法即可

/**
 * 	如果需要监听,可以将多个接口定义用一个类来实现
 *	例如此处通过自定义适配器实现MouseListener、MouseMotionListener、
 *	MouseWheelListener三个接口, 随后在使用监听的时候用自定义的MyouseAdpater
 *	类,根据自己的需求重写某些方法即可(从而不需要每次实现某个接口都重写其对应的所有抽象方法)
 *
 */
public class MyMouseAdapter implements MouseListener,MouseMotionListener,MouseWheelListener{
	@Override
	public void mouseWheelMoved(MouseWheelEvent e) {
		// TODO Auto-generated method stub
	}
	@Override
	public void mouseDragged(MouseEvent e) {
		// TODO Auto-generated method stub
	}
	@Override
	public void mouseMoved(MouseEvent e) {
		// TODO Auto-generated method stub
	}
	@Override
	public void mouseClicked(MouseEvent e) {
		// TODO Auto-generated method stub
	}
	@Override
	public void mousePressed(MouseEvent e) {
		// TODO Auto-generated method stub
	}
	@Override
	public void mouseReleased(MouseEvent e) {
		// TODO Auto-generated method stub
	}
	@Override
	public void mouseEntered(MouseEvent e) {
		// TODO Auto-generated method stub
	}
	@Override
	public void mouseExited(MouseEvent e) {
		// TODO Auto-generated method stub
	}
}

/**
 *	在测试中除却可以使用自定义的适配器实现相关功能,java还提供了相同的一个适配器,
 *	从而可以直接使用相应的适配器进行操作即可(MouseAdpater)
 */
public class MyMouseAdpaterTest {
	public JFrame jf = new JFrame();
	JButton jb = new JButton();
	public void init()
	{
		jf.setBounds(200, 200, 400, 300);
		jb.addMouseListener(new MyMouseAdapter(){
			//重写mouseClicked方法
			@Override
			public void mouseClicked(MouseEvent e) {
				System.out.println("鼠标点击...");
			}
		});
		jb.addMouseMotionListener(new MouseAdapter(){
			@Override
			public void mouseDragged(MouseEvent e) {
				System.out.println("鼠标拖动...");
			}
		});
		/**
		 * 此处要注意的是在使用适配器时必须是要在指定的监听器下重写指定的方法才有效
		 */
		jf.add(jb);
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	public static void main(String[] args) {
		new MyMouseAdpaterTest().init();
	}
}

常用的Adapter类

Adapter对应的EventListener
java.awt.event.ComponentAdapterComponentListener
java.awt.event.ContainerAdapterContainerListener
java.awt.event.FocusAdapterFocusListener
java.awt.event.KeyAdapterKeyListener
java.awt.event.MouseAdapterMouseListener
java.awt.event.MouseMotionAdapterMouseMotionListener
java.awt.event.WindowAdapterWindowListener

​ PS:在java中提供两个方法用以辨别到底是哪个组件触发了事件,分别为getSource()getActionCommand()getActionCommand()方法是ActionEvent类所提供,而getSource()方法是EventObject类所提供,但ActionEvent类继承了EventObject类,因此这两个方法ActionEvent都可以使用

🔖键盘事件

public class KeyListenerTest {
	public JFrame jf = new JFrame();
	public JButton jb = new JButton();
	public void init()
	{
		jf.setSize(500, 400);
		jb.addKeyListener(new KeyListener(){
			@Override
			public void keyTyped(KeyEvent e) {
				System.out.println("键盘输入...");
			}
			@Override
			public void keyPressed(KeyEvent e) {
				System.out.println("键盘按下...");
				/**
				 * 获取键盘按下的是哪个键
				 * getKeyChar方法可能无法识别某些字符
				 * getKeyCode方法比较通用
				 */
				System.out.println(e.getKeyChar());
				System.out.println(e.getKeyCode());
				/**
				 * 通过获得键盘的输入,判断键盘按下的是哪个键
				 * KeyEvent.VK_UP:上键
				 * KeyEvent.VK_DOWN:下键
				 * KeyEvent.VK_LEFT:左键
				 * KeyEvent.VK_RIGHT:右键
				 * KeyEvent.VK_F1:F1键
				 * ...
				 * KeyEvent.VK_...
				 */
				if(e.getKeyCode()==KeyEvent.VK_UP)
					System.out.println("上");
				else if(e.getKeyCode()==KeyEvent.VK_DOWN)
					System.out.println("下");
				else if(e.getKeyCode()==KeyEvent.VK_LEFT)
					System.out.println("左");
				else if(e.getKeyCode()==KeyEvent.VK_RIGHT)
					System.out.println("右");
				else if(e.getKeyCode()==KeyEvent.VK_F1)
					System.out.println("F1");
			}
			@Override
			public void keyReleased(KeyEvent e) {
				System.out.println("键盘释放...");
			}
		});
		jf.add(jb);
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	public static void main(String[] args) {
		new KeyListenerTest().init();
	}
}

🔖简易计算器的实现

设计思路分析:

a.实现简易计算器面板的设计

  • 结合本机计算器实现相关设计,涉及菜单栏、按钮、文本框,结合需求进行相关定义

  • 可以用String[ ](String类型的数组)存储按钮名称,用JButton[ ](JButton类型的数组)存储每一组按钮,之后再涉及判断的时候只需要遍历查找相应的按钮名称便可进行指定的操作。

  • 面板设计:此处分为菜单栏、文本框、按钮

    • 菜单栏包括文件(新建、保存记录、打开记录、关闭)、工具(标准、科学、程序员、日期计算)

    • 文本框设计两个,上方的文本框用以显示计算过程,下方的文本框用以接收用户输入的数据以及显示最终计算的结果

    • 按钮部分根据按钮的外观设计可分为两类进行设计,第一排的按钮(M..)、下方的按钮(数字、运算符)

    完成基本面板的设计则可通过相应的方法对计算器面板进行美化,进一步贴切真正的计算器

b.实现对基本按钮的监听

​ 要完成对按钮的监听,则可通过定义一个固定的监听器进行操作,在其中获得相应的指令,一旦用户触发了某个按钮,便调用相应按钮的某个方法进行响应,从而简化了许多冗余的代码,显得简洁明了而不冗长,之后的学习也要按照这种思路和技巧,不要一昧地一头钻

// 定义监听器,用于监听所有的按钮操作,一旦满足条件则执行相应的内容
ActionListener al = new ActionListener( )
 {
			@Override
			public void actionPerformed(ActionEvent e) {
				//获取指定的命令
				String str = e.getActionCommand();
				if(触发某个按钮)
				{
					触发了某个按钮则调用相应方法执行相关的操作进行响应
				}else if(...)
        {
           //执行相应的操作
        }
      }
}

c.设计一个简易计算器,实现基本的数据运算,需注意以下细节

​ 注意计算的位数不能够超过文本框的长度:在接收数据的输入时限制数据的输入位数,当输入位数大于限制的计算位数则不执行操作(即无论如何触发按钮都不会继续添加内容,文本框中还是原来的数据)

​ 注意开头0的个数(开头只能有一个0,小数点之后的内容另议):即判断如果当前接收数据的文本框的内容长度等于1且为0时不能够再添加0(将当前触发的按钮数据直接赋给文本框)

​ 注意小数点的位置和个数:小数点不能在开头第一位,有且只有一个

​ 注意正负号的问题:正负号问题可以用“原来的数据*(-1)”解决,从而得到数据相应的相反数

​ 有关于退格问题:退格问题则是将文本框的内容最后的字符截断(舍弃),可以用Strng字符串的subString方法实现

​ 用上方的文本框记录计算过程,当触发了相关的运算符则将当前下方文本框的接收的数据记录在上方的文本框,随后接收下一个数据的输入,当触发了‘=’时最终在下方的文本框中显示最终的计算结果,并清空上方的文本框内容。并可以实现多次计算。

​ 可以用一个中间变量存放中间的计算结果,此外,定义一个boolean类型的变量用于判断是第几个数据输入,从而执行不同的操作

public class SimpleCalculator {
	/**
	 * 设计一个简易计算器,实现基本的数据运算,需注意以下细节
	 * 1.注意计算的位数不能够超过文本框的长度
	 * 2.注意开头0的个数(开头只能有一个0,小数点之后的内容另议)
	 * 3.注意小数点的位置和个数
	 * 4.注意正负号的问题
	 * 5.有关于退格问题
	 */
	//1.首先设计简易计算器的面板
	private JFrame jf = new JFrame("简易计算器");
	//定义第1行的设计(用按钮实现)
	private String[] s1 = {"MC","MR","M+","M-","MS"};
	private JButton[] bt1 = new JButton[s1.length];
	//定义下方的按钮内容
	private String[] s2 = {"%","√","^2","(1/x)","CE","C","◀","÷",
						   "7","8","9","×","4","5","6","-","1","2",
						   "3","+","±","0",".","="};
	//将按钮存储在JButton数组中方便进行操作
	private JButton[] bt2 = new JButton[s2.length];
	//设置两个文本框用以显示数据,前者用以显示数据,后者用以接收数据并输出结果
	private JTextField jt1 = new JTextField(20);
	private JTextField jt2 = new JTextField("0",20);
	//创建菜单栏(仿照本机的计算器进行设置)
	//创建一个按钮,点击按钮则显示相应的右键菜单栏
	private JButton jb = new JButton("☰");
	private JPopupMenu pop = new JPopupMenu();
	private JMenu file = new JMenu("文件");
	private JMenuItem newFile = new JMenuItem("新建");
	private JMenuItem saveFile = new JMenuItem("保存记录");
	private JMenuItem openFile = new JMenuItem("打开记录");
	private JMenuItem close = new JMenuItem("关闭");
	private JMenu tool = new JMenu("工具");
	private JMenuItem standard = new JMenuItem("标准");
	private JMenuItem science = new JMenuItem("科学");
	private JMenuItem programer = new JMenuItem("程序员");
	private JMenuItem dateCal = new JMenuItem("日期计算");
	private JLabel jl = new JLabel("标准");
	public String num1 ;//记录数据1
	public String num2 ;//记录数据2
	public String result;//记录中间结果
	public boolean flag = true;//判断输入的是否为第2个操作数
	public void init(){
		//设计计算器面板
		//先放置第一行按钮
		JPanel jp1 = new JPanel();
		jp1.setLayout(new GridLayout(1,5));
		for(int i=0;i<s1.length;i++)
		{
			bt1[i] = new JButton(s1[i]);
			bt1[i].setBackground(new Color(240,240,240));
			bt1[i].setForeground(Color.red);//设置按钮字体颜色
			bt1[i].setFocusPainted(false);//去除按钮文字边框
			bt1[i].setBorderPainted(false);//去除按钮边框
			jp1.add(bt1[i]);
		}
		//放置剩余的按钮
		JPanel jp2 = new JPanel();
		jp2.setLayout(new GridLayout(6,4,2,2));
		for(int i=0;i<s2.length;i++)
		{	
			bt2[i] = new JButton(s2[i]);
			if((int)s2[i].charAt(0)>='0'&&(int)s2[i].charAt(0)<='9')
			{
				bt2[i].setBackground(Color.white);//设置数字背景颜色
				Font font = new Font("华文行楷", Font.PLAIN, 20);
				bt2[i].setFont(font);//设置文字样式
				bt2[i].setFocusPainted(false);//去除按钮文字边框
			}
			else
			{
				bt2[i].setBackground(new Color(245,245,245));
				bt2[i].setFocusPainted(false);//去除按钮文字边框
			}
			jp2.add(bt2[i]);
		}
		//将两个文本框放置在一个面板中
		JPanel jp3 = new JPanel();
		jp3.setLayout(new GridLayout(3,1,0,0));
		//设置菜单栏的相关属性(将菜单栏隐藏,鼠标点击相应的按钮之后再显示)
		file.add(newFile);
		file.add(saveFile);
		file.add(openFile);
		file.add(close);
		tool.add(standard);
		tool.add(science);
		tool.add(programer);
		tool.add(dateCal);
		pop.add(file);
		pop.addSeparator();
		pop.add(tool);
		//为菜单栏按钮添加监听器
		jb.addMouseListener(new MouseAdapter() {
			@Override
			public void mouseClicked(MouseEvent e) {
				if(e.getButton()==MouseEvent.BUTTON1)
				{
					pop.show(jp3,e.getX(),e.getY());
				}
			}
		});
		JPanel jp = new JPanel();
		jp.setLayout(new FlowLayout(FlowLayout.LEFT));
		jb.setFocusPainted(false);  //去除按钮文本焦点
		jb.setBorderPainted(false); //去除按钮边框  
		jb.setBackground(new Color(240,240,240));
		jp.add(jb);
		jp.add(jl);
		//设置文本框的对齐方式为右对齐
		jt1.setHorizontalAlignment(SwingConstants.RIGHT);
		jt2.setHorizontalAlignment(SwingConstants.RIGHT);
		//去除文本框按钮边框,设置相应的背景颜色
		jt1.setBorder(null);
		jt2.setBorder(null);
		jt1.setBackground(new Color(240,240,240));
		jt2.setBackground(new Color(240,240,240));
		jp3.add(jp);
		jp3.add(jt1);
		jp3.add(jt2);
		//将相应面板加载到窗体中
		jf.add(jp3,BorderLayout.NORTH);
		jf.add(jp1,BorderLayout.CENTER);
		jf.add(jp2,BorderLayout.SOUTH);
		jf.pack();
		jf.setResizable(false);
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	public void calculator()
	{
		//初始化计算器面板
		init();
		/**
		 * 为相应的菜单栏操作添加监听器,将相应的菜单栏操作监听封装到一个方法中
		 * 随后将相应的组件添加监听即可
		 */
		//定义监听器,用于监听所有的按钮操作,一旦满足条件则执行相应的内容
		ActionListener al = new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				//获取指定的命令
				String str = e.getActionCommand();
				if(str.equals("新建"))
				{
					//将文本框对应的所有的内容清空
					jt1.setText(null);
					jt2.setText("0");
				}
				else if(str.equals("保存记录"))
				{
					FileDialog fd = new FileDialog(jf,"保存文件",FileDialog.SAVE);
					fd.setVisible(true);
				}
				else if(str.equals("打开记录"))
				{
					FileDialog fd = new FileDialog(jf,"打开文件",FileDialog.LOAD);
					fd.setVisible(true);
					System.out.println("打开文件:"+fd.getDirectory()+"--"+fd.getFile());
				}
				else if(str.equals("关闭"))
				{
					System.exit(0);
				}
				else if(str.equals("标准"))
				{
					jl.setText("标准");
				}
				else if(str.equals("科学"))
				{
					jl.setText("科学");
				}
				else if(str.equals("程序员"))
				{
					jl.setText("程序员");
				}
				else if(str.equals("日期计算"))
				{
					jl.setText("日期计算");
				}
				else if(str.equals("CE"))
				{
					//用户按下了CE键
					jt2.setText("0");
				}
				else if(str.equals("C"))
				{
					//用户按下了清除键
					HandleC();
				}
				else if(str.equals("◀"))
				{
					//用户按下了退格键
					HandleDel();
				}
				else if(str.equals("±"))
				{
					//用户按下了正负号
					HandelSign();
				}
				else if("0123456789.".indexOf(str)>=0)
				{
					//用户按下了数字或者是小数点
					HandleNumber(str);
				}
				else
				{
					//用户按下了运算符(+、-、*、/、%、^2、(1/x)、=)
					HandleOperator(str);
				}
			}
			/**
			 * 定义处理各种事件的方法
			 */
			//1.处理清除键C
			private void HandleC() {
				jt1.setText(null);
				jt2.setText("0");
			}
			//2.处理退格键
			private void HandleDel() {
				if(jt2.getText().length()>0)
					jt2.setText(jt2.getText().substring(0, jt2.getText().length()-1));
				else
					jt2.setText("0");
			}
			//3.处理正负号±
			private void HandelSign() {
				BigDecimal b1 = new BigDecimal(jt2.getText());
				BigDecimal b2 = new BigDecimal("-1");
				jt2.setText(b1.multiply(b2).toString());
			}
			//4.处理数字和小数点
			private void HandleNumber(String key) {
				if(!flag)//如果输入的是第二个数据,则需要清空jt2的内容
				{
					jt2.setText("0");
					flag=true;//置位
				}
				//如果开头是数字,要保证以只能够有一个0开头
				if(jt2.getText().length()==1&&jt2.getText().startsWith("0"))
					jt2.setText(key);
				else if(key.equals(".")&&!jt2.getText().contains("."))
					jt2.setText(jt2.getText().concat("."));
				//注意计算的位数不能够超过文本框的长度,此处设计计算位数最多20 
				else if(key.charAt(0)>='0'&&key.charAt(0)<='9'&&jt2.getText().length()<=20)
					jt2.setText(jt2.getText().concat(key));
			}
			//5.处理运算符
			private void HandleOperator(String str) {
				/**
				 * 对重复代码进行封装
				 * 如果是基本的加减乘除则进行封装
				 */
				if(!str.equals("="))
				{
					//记录数据
					num1 = jt2.getText();
					jt1.setText(jt2.getText().concat(str));
					flag = false;
				}
				else
				{
					//获取第二个数据及相应的运算符进行操作
					num2 = jt2.getText();
					char ch = jt1.getText().charAt(jt1.getText().length()-1);
					System.out.println(ch);
					switch(ch)
					{
						case'+':
						{
							result = BigDecimalCal.add(num1, num2).toString();
							break;
						}
						case'-':
						{
							result = BigDecimalCal.sub(num1, num2).toString();
							break;
						}
						case'×':
						{
							result = BigDecimalCal.mul(num1, num2).toString();
							break;
						}
						case'÷':
						{
							result = BigDecimalCal.div(num1, num2).toString();
							result.replaceAll("(\\d+)0*", "$1");
							break;
						}
						case'%':
						{
							result = Double.valueOf(num1)%Double.valueOf(num2)+"";
							break;
						}
						default:
							break;
					}
					jt1.setText(null);
					jt2.setText(result);
					flag=false;
				}
			   if(str.equals("(1/x)"))
				{
					result = BigDecimalCal.div("1", num1).toString();
					jt1.setText(null);
					jt2.setText(result);
					flag=false;
				}
				if(str.equals("^2"))
				{
					result = BigDecimalCal.mul(num1, num1).toString();
					jt1.setText(null);
					jt2.setText(result);
					flag=false;
				}
				if(str.equals("√"))
				{
					result = Math.sqrt(Double.valueOf(num1))+"";
					jt1.setText(null);
					jt2.setText(result);
					flag=false;
				}
			}
		};
		newFile.addActionListener(al);
		saveFile.addActionListener(al);
		openFile.addActionListener(al);
		close.addActionListener(al);
		standard.addActionListener(al);
		science.addActionListener(al);
		programer.addActionListener(al);
		dateCal.addActionListener(al);
		for(int i=0;i<s1.length;i++)
		{
			bt1[i].addActionListener(al);
		}
		for(int i=0;i<s2.length;i++)
		{
			bt2[i].addActionListener(al);
		}
	}
	public static void main(String[] args) {
		new SimpleCalculator().calculator();
	}
}
public class BigDecimalCal {
	/**
	 * 利用BigDecimal实现基本的加减乘除运算
	 */
	public static BigDecimal add(String a,String b)
	{
		BigDecimal b1 = new BigDecimal(a);
		BigDecimal b2 = new BigDecimal(b);
		return b1.add(b2);
	}
	public static BigDecimal sub(String a,String b)
	{
		BigDecimal b1 = new BigDecimal(a);
		BigDecimal b2 = new BigDecimal(b);
		return b1.subtract(b2);
	}

	public static BigDecimal mul(String a,String b)
	{
		BigDecimal b1 = new BigDecimal(a);
		BigDecimal b2 = new BigDecimal(b);
		return b1.multiply(b2);
	}

	public static BigDecimal div(String a,String b)
	{
		BigDecimal b1 = new BigDecimal(a);
		BigDecimal b2 = new BigDecimal(b);
		return b1.divide(b2,5,3);
	}
}

实现样例参考:

5.菜单

​ 与菜单相关的3个主要内容:菜单条、菜单、菜单项

【1】顶部菜单

public class TopMenuTest {
	private JFrame jf = new JFrame();
	//创建菜单条
	private JMenuBar jmb = new JMenuBar();
	//创建菜单
	private JMenu file = new JMenu("文件");
	private JMenu edit = new JMenu("编辑");
	//创建相应的菜单项
	private JMenuItem newFile = new JMenuItem("新建");
	private JMenuItem openFile = new JMenuItem("打开");
	private JMenuItem saveFile = new JMenuItem("保存");
	private JMenuItem exitFile = new JMenuItem("退出");
	
	private JMenuItem copyFile = new JMenuItem("复制");
	private JMenuItem prasteFile = new JMenuItem("粘贴");
	private JMenuItem findFile = new JMenuItem("查找");
	
	//设置子菜单
	private JMenu format = new JMenu("格式");
	private JMenuItem newFormat = new JMenuItem("新增格式");
	private JMenuItem cancelFormat = new JMenuItem("取消格式");
	
	//创建文本域
	private JTextArea jta = new JTextArea(10, 30);
	public void init()
	{
		//将菜单项依次添加到指定的菜单中
		file.add(newFile);
		file.add(openFile);
		file.add(saveFile);
		file.add(exitFile);
		edit.add(copyFile);
		edit.add(prasteFile);
		edit.add(findFile);
		//添加分隔符
		edit.addSeparator();
		//将子菜单的菜单项依次添加到指定的子菜单,随后再将子菜单添加到指定的菜单中
		format.add(newFormat);
		format.add(cancelFormat);
		edit.add(format);
		//将菜单加载到菜单条中
		jmb.add(file);
		jmb.add(edit);
		//将菜单条挂载到窗体中:setJMenuBar
		jf.setJMenuBar(jmb);
		//将文本域加载到窗体中
		jf.add(jta);
		//设置窗体的相关属性
		jf.setVisible(true);
		jf.pack();
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	public void listener()
	{
		//初始化窗体
		init();
		//为指定的事件设置监听器
		ActionListener al = new ActionListener() {
			//将所有的内容封装到一个方法中,通过指定组件添加相应的监听事件
			@Override
			public void actionPerformed(ActionEvent e) {
				//定义变量获取监听的事件信息
				String str = e.getActionCommand();
				if(str.equals("打开"))
				{
					FileDialog fd = new FileDialog(jf,"打开文件",FileDialog.LOAD);
					fd.setVisible(true);
					System.out.println("打开文件:"+fd.getDirectory()+"--"+fd.getFile());
				}
				else if(str.equals("保存"))
				{
					FileDialog fd = new FileDialog(jf,"打开文件",FileDialog.SAVE);
					fd.setVisible(true);
				}
				else if(str.equals("退出"))
				{
					System.exit(0);
				}
				else
				{
					System.out.println("其他操作...");
				}
			}
		};
		newFile.addActionListener(al);
		openFile.addActionListener(al);
		saveFile.addActionListener(al);
		exitFile.addActionListener(al);
	}
	public static void main(String[] args) {
		new TopMenuTest().listener();
	}
}

【2】右键菜单

public class TopMenuTest {
	private JFrame jf = new JFrame();
	//创建菜单条
	private JPopupMenu  pop = new JPopupMenu();
	//创建菜单
	private JMenu file = new JMenu("文件");
	private JMenu edit = new JMenu("编辑");
	//创建相应的菜单项
	private JMenuItem newFile = new JMenuItem("新建");
	private JMenuItem openFile = new JMenuItem("打开");
	private JMenuItem saveFile = new JMenuItem("保存");
	private JMenuItem exitFile = new JMenuItem("退出");
	
	private JMenuItem copyFile = new JMenuItem("复制");
	private JMenuItem prasteFile = new JMenuItem("粘贴");
	private JMenuItem findFile = new JMenuItem("查找");
	
	//设置子菜单
	private JMenu format = new JMenu("格式");
	private JMenuItem newFormat = new JMenuItem("新增格式");
	private JMenuItem cancelFormat = new JMenuItem("取消格式");
	
	//创建文本域
	private JTextArea jta = new JTextArea(10, 30);
	public void init()
	{
		//将菜单项依次添加到指定的菜单中
		file.add(newFile);
		file.add(openFile);
		file.add(saveFile);
		file.add(exitFile);
		edit.add(copyFile);
		edit.add(prasteFile);
		edit.add(findFile);
		//添加分隔符
		edit.addSeparator();
		//将子菜单的菜单项依次添加到指定的子菜单,随后再将子菜单添加到指定的菜单中
		format.add(newFormat);
		format.add(cancelFormat);
		edit.add(format);
		//将菜单加载到右键菜单条中
		pop.add(file);
		pop.add(edit);
		//定义画板JPanel用于存放右键菜单并设置监听
		JPanel jp = new JPanel();
		jp.setPreferredSize(new Dimension(300,160));
		jp.add(pop);
		jp.addMouseListener(new MouseAdapter(){
			@Override
			public void mouseReleased(MouseEvent e) {
				if(e.isPopupTrigger())
					//如果用户点击了右键,则在jp(画板上)指定的位置显示右键菜单栏
					pop.show(jp,e.getX(),e.getY());
			}
		});
		//将文本域加载到窗体中
		jp.add(jta);
		jf.add(jp);
		//设置窗体的相关属性
		jf.setVisible(true);
		jf.pack();
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	public void listener()
	{
		//初始化窗体
		init();
		//为指定的事件设置监听器
		ActionListener al = new ActionListener() {
			//将所有的内容封装到一个方法中,通过指定组件添加相应的监听事件
			@Override
			public void actionPerformed(ActionEvent e) {
				//定义变量获取监听的事件信息
				String str = e.getActionCommand();
				if(str.equals("打开"))
				{
					FileDialog fd = new FileDialog(jf,"打开文件",FileDialog.LOAD);
					fd.setVisible(true);
					System.out.println("打开文件:"+fd.getDirectory()+"--"+fd.getFile());
				}
				else if(str.equals("保存"))
				{
					FileDialog fd = new FileDialog(jf,"打开文件",FileDialog.SAVE);
					fd.setVisible(true);
				}
				else if(str.equals("退出"))
				{
					System.exit(0);
				}
				else
				{
					System.out.println("其他操作...");
				}
			}
		};
		newFile.addActionListener(al);
		openFile.addActionListener(al);
		saveFile.addActionListener(al);
		exitFile.addActionListener(al);
	}
	public static void main(String[] args) {
		new TopMenuTest().listener();
	}
}

6.位图与绘图

【1】简单绘图

绘图是开发游戏和一些复杂界面的基础。脑子里有这样一个概念即可:

  • 用什么样的笔去画图,这支笔在哪里来?

  • 在什么位置画图,画图板是用什么构成的?

  • 谁来调用画图动作,连续的动作是不是就是动画?

在component中,有三个和画图有关的方法

paint(Graphics g)update(Graphics g)repaint()

其实,repaint 调用update,update调用paint(掌握几个问题即可)

画笔是什么?

Graphics类,有这个类的对象就可以画图

这个笔怎么来,能去new出来吗?

不可以,只有在paint和update方法中才有这个对象,不要自己去new**,**另外通过BufferedImage也可以获得画笔。

在什么位置画图?

Canvas类、Jpanel类都可以,用画图板中的paint方法中就可以获得画笔

谁来调用画图动作?

调用画图板的repaint方法即可,连续的动作就是动画!

画笔来自两个地方(来自画图板、来自显存):笔来自于哪里就画到哪里

​ Graphics是一个抽象的画笔对象,Graphics可以在组件上绘制丰富多彩的几何图形和位图。Graphics类提供了如下几个方法用于绘制几何图形和位图。

drawLine():绘制直线

drawString():绘制字符串

drawRect():绘制矩形

drawRoundRect():绘制圆角矩形

drawOval():绘制椭圆形状

drawPloygon():绘制多边形

drawArc():绘制一段弧线

drawPloyline():绘制一段折线

fillRect():填充一个矩形

fillRoundRect():填充一个圆角矩形区域

fillOval():填充椭圆区域

fillPloygon():填充多边形

fillArc():填充圆弧两个端点的连线跟圆弧所包裹起来的区域。

drawImage():绘制位图。

​ 除此之外,Graphics还提供了setColor()setFont()两个方法用于设置画笔的颜色和字体,其中setColor( )方法需要传入一个参数Color参数,它可以使用RGB、CMYK等方式设置一个颜色,而setFont( )方法需要传入一个Font参数,Font参数需要制定字体名、字体样式、字体大小三个属性。

​ AWT专门提供了一个Canvas类作为绘图的画布,程序可以通过创建Canvas的子类,并重写它的paint()方法来实现绘图。

public class GraphTest {
	public boolean isPress = false;
	public void init()
	{
		JFrame jf = new JFrame("图形测试");
		jf.setSize(600, 400);
		//定义绘图板
		MyDrawArea mda = new MyDrawArea();
		jf.add(mda);
		//定义按钮用于显示绘画
		JButton bt = new JButton("测试绘画...");
		bt.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				isPress = true;
				mda.repaint();
			}
		});
		jf.add(bt,BorderLayout.NORTH);
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	//定义自定义画板,继承JPanel类
	class MyDrawArea extends JPanel {
		@Override
		public void paint(Graphics g) { // Graphics 画笔
			if(isPress) {
			// 设置画笔的颜色
			g.setColor(new Color(230, 150, 78));
			g.drawRect(200, 300, 100, 100);
				
			g.setColor(new Color(20, 250, 178));
			g.fillOval(20, 30, 200, 300);
				
			g.setColor(new Color(130, 10, 118));
			g.drawLine(300, 300, 500, 500);
				
			g.setColor(new Color(130, 10, 118));
			g.drawLine(500, 500, 300, 400);
			}
		}
	}
	public static void main(String[] args) {
		new GraphTest().init();
	}
}

练习:漫天繁星点点

/**
 * 	1.清除填充色
 * 	clearRect(int x,int y,int width,int height)
  	通过使用当前绘图表面的背景色进行填充来清除指定的矩形。
 *	2.绘制图形常用方法	
 *	drawOval(int x,int y,int width,int height):绘制椭圆的边框
 *	drawRect(int x,int y,int width,int height):绘制指定矩形的边框
 *	drawLine(int x1,int y1,int x2,int y2)
 	在此图形上下文的坐标系中,使用当前颜色在点 (x1, y1) 和 (x2, y2) 之间画一条线
 	3.填充图形颜色常用方法
 *	fillOval(int x,int y,int width,int height):使用当前颜色填充外接指定矩形框的椭圆
 *	fillRect(int x,int y,int width,int height):填充指定的矩形
 *	fillRoundRect(int x,int y,int width,int height,int arcWidth,int arcHeight)
 	用当前颜色填充指定的圆角矩形
 	4.设置颜色和字体
 *	setColor(Color c):将此图形上下文的当前颜色设置为指定颜色
 *	setFont(Font font):将此图形上下文的字体设置为指定字体。 
 */
public class MyStar {
	public static void main(String[] args) {
		new MyStar().init();
	}
	public void init()
	{
		JFrame jf = new JFrame("繁星点点");
		jf.setSize(1366,768);
		jf.setBackground(Color.black);
		MyPanel mp= new MyPanel();
		jf.add(mp);
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		
	}
}
class MyPanel extends JPanel
{
	@Override
	public void paint(Graphics g) {
		//画满天星星
		g.setColor(Color.white);
		for(int i=0;i<300;i++)
		{
			g.drawString("*", (int)(Math.random()*1366), (int)(Math.random()*768));
		}
		//画月亮,用两个椭圆覆盖实现
		g.setColor(Color.yellow);
		g.drawOval(100, 100, 200, 125);
		g.fillOval(100, 100, 200, 125);
		g.setColor(Color.black);
		g.drawOval(150, 100, 200, 125);
		g.fillOval(150, 100, 200, 125);
		//画相应的情景:两个小人
		g.setColor(Color.blue);
		g.drawOval(1000, 500, 50, 50);
		g.drawOval(1010, 510, 10, 10);
		g.drawOval(1030, 510, 10, 10);
		g.drawOval(1012, 550, 25, 75);
		g.drawString("⊹", 1020, 535);
		g.drawLine(1010, 570, 980, 600);
		g.drawLine(1040, 570, 1070, 600);
		g.drawLine(1010, 610, 980, 640);
		g.drawLine(1040, 610, 1070, 640);

		g.setColor(Color.pink);
		g.drawOval(1100, 500, 50, 50);
		g.drawOval(1110, 510, 10, 10);
		g.drawOval(1130, 510, 10, 10);
		g.drawOval(1112, 550, 25, 75);
		g.drawString("⊹", 1120, 535);
		g.drawLine(1110, 570, 1080, 600);
		g.drawLine(1140, 570, 1170, 600);

		g.drawLine(1110, 610, 1080, 640);
		g.drawLine(1140, 610, 1170, 640);
		g.setColor(Color.white);
		g.drawString("♒♒", 1110, 507);
		
		//画个小房子
		g.drawLine(300, 400, 100, 500);
		g.drawLine(300, 400, 500, 500);
		g.drawLine(100, 500, 500, 500);
		g.drawRect(150, 500, 300, 150);
		g.drawRect(200, 550, 50, 50);
		g.drawLine(200, 575, 250, 575);
		g.drawLine(225, 550, 225, 600);
		g.drawRect(300, 600, 25, 50);
		g.drawRect(325, 600, 25, 50);
	}
}

【2】绘图

ImageIO输入/输出位图

​ 如果绘制磁盘上的GIF、JPG等格式的位图,则需要利用ImageIO工具类。ImageIO利用ImageReader和ImageWriter读写图形文件,对于底层的实现细节我们无须关注,只需要学会怎样利用该工具类来读写图形文件即可。

​ ImageIO类并不支持所有格式的图形文件,但是可以支持大多数图形文件。可以通过ImageIO类的如下几个静态方法来来操作它所支持的几种格式的图形文件。

static String[] getReaderFileSuffixes():返回一个String数组,该数组列出ImageIO所有能读的图形文件的文件后缀名。

static String[] getReaderFormatNames():返回一个String数组,该数组列出ImageIO所有能读的图形文件的非正式格式名称。

static String[] getWriterFileSuffixes():返回一个String数组,该数组列出ImageIO所有能写的图形文件的后缀名

static String[] getWriterFormatNames():返回一个String数组,该数组列出ImageIO所有能写的图形文件的非正式格式名称。

ImageIO所支持的大部分格式为:bmp、jpg、jpeg、wbmp、png、gif(正式格式名称)

BMP、JPG、JPEG、GIF(非正式格式名称)

/**
 *	drawImage(Image img,int x,int y,Color bgcolor,ImageObserver observer)
 	绘制指定图像中当前可用的图像
 *	drawImage(Image img,int x,int y,ImageObserver observer)
 	绘制指定图像中当前可用的图像
 *	drawImage(Image img,int x,int y,int width,int height,Color bgcolor,ImageObserver observer)
 	绘制指定图像中已缩放到适合指定矩形内部的图像
 *	abstract  boolean drawImage(Image img,int x,int y,int width,int height,ImageObserver observer) 
 	绘制指定图像中已缩放到适合指定矩形内部的图像
 *	drawImage(Image img,int dx1,int dy1,int dx2,int dy2,int sx1,int sy1,int sx2,int sy2,Color bgcolor,ImageObserver observer) 
 	绘制当前可用的指定图像的指定区域,动态地缩放图像使其符合目标绘制表面的指定区域。 
 *	drawImage(Image img,int dx1,int dy1,int dx2,int dy2,int sx1,int sy1,int sx2,int sy2,ImageObserver observer) 
 	绘制当前可用的指定图像的指定区域,动态地缩放图像使其符合目标绘制表面的指定区域
 */
public class ImageTest {
	public static void main(String[] args) {
		new ImageTest().init();
	}
	public boolean isPress = false;
	//加载一张图片
	ImageIcon image = null;
	public void init()
	{
		//加载的图片可以是静态图片也可以是动态图片
		image = new ImageIcon("pictures/timg2.gif");
		JFrame jf = new JFrame("测试加载图片...");
		jf.setSize(600, 400);
		JButton bt = new JButton("测试");
		bt.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				isPress = true;
			}
		});
		MyPanel mp = new MyPanel();
		jf.add(mp);
		jf.add(bt,BorderLayout.NORTH);
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	class MyPanel extends JPanel
	{
		public void paint(Graphics g)
		{
			if(isPress)
			{
				//每次均重绘场景,从而更好地显示动图效果
				g.setColor(Color.white);
				g.fillRect(0, 0, 600, 400);
				g.drawImage(image.getImage(), 30, 30, this);
			}
		}
	}
}

🔖Timer类

public class TimerTest {
	public static void main(String[] args) {
		JFrame jf =new JFrame("测试");
		jf.setSize(600, 400);
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//每隔一秒打印一次Hello 
		//Timer应该导入的是java.swing.Timer
		Timer timer =new Timer(200, new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				System.out.println("hello");
			}
		});
		timer.start();
	}
}

🔖图形移动

public class ImageMoveTest {
	public boolean isPress = false;
	//加载一张图片
	public ImageIcon image = null;
	//定义图片加载的位置(模拟图片移动)
	public int x = 0;
	public int y = 0;
	public void init()
	{
		image = new ImageIcon("pictures/timg2.gif");
		JFrame jf = new JFrame("测试图片移动");
		jf.setBackground(Color.white);
		jf.setSize(600,400);
		MyPanel mp = new MyPanel();
		jf.add(mp);
		JButton bt = new JButton("gogo");
		bt.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				isPress = true;
				mp.repaint();
			}
		});
		jf.add(bt,BorderLayout.SOUTH);
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//每隔200ms改变坐标位置
		Timer timer =new Timer(200, new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				 x+=10;
				 y+=10;
			}
		});
		timer.start();
	}
	class MyPanel extends JPanel
	{
		public void paint(Graphics g)
		{
			if(isPress)
			{
				g.setColor(Color.white);
				g.fillRect(0, 0, 600, 400);
				g.drawImage(image.getImage(), x, y, 75, 75, this);
			}
		}
	}
	public static void main(String[] args) {
		new ImageMoveTest().init();
	}
}

【3】处理位图

​ AWT不仅仅可以绘制一些简单的几何图形,同样也可以绘制位图。Graphics类提供了一个drawImage()方法用来绘制位图,该方法需要一个Image对象参数。

​ Image类是一个抽象类,无法直接创建Image对象,为此Java提供了一个BufferedImage的子类,这个子类是一个可访问图像数据缓冲区的Image实现类。该类提供了一个简单的构造器,用于创建BufferedImage对象。

BufferedImage(int width,int height,int imageType):创建指定大小、指定图像类型的BufferedImage对象。

​ BufferedImage对象还提供了一个getGraphics()方法返回该对象的Graphics对象,通过此对象可以向image中添加图形。

​ 借助BufferedImage对象,可以实现AWT中实现缓冲技术——当需要向GUI组件上绘制图像时,如果图像较大那么往组件上绘图时,有可能会因为加载不完全而导致在组件上显示的效果是图像一点一点的画完,不能一次性的将图片画出来。通过BufferedImage对象,我们可以先将图片加载到此对象中,加载的过程在内存中实现,过程我们不需要关注,我们要看的只是结果,当图像全部加载完成,再通过此对象一次性将图片绘制到组件上。

7.高级组件

【1】Swing中的高级组件

🔖JToolBar

用来创建工具条,一般有一些方法:

JButton add(Action a) :添加按钮

void addSeparator(Dimension size) :向工具栏的末尾添加指定大小的分隔符

void setFloatable(boolean b) : 工具条是否可以浮动

void setMargin(Insets m) :设置工具条边框和按钮之间的页边距

void setOrientation(int o) :设置工具条方向

void setRollover(boolean rollover) :设置工具栏是否是rollover状态

add方法分析:

​ Action 接口是ActionListener接口的子接口,除了actionPerformed方法还有name和icon两个属性。Action不仅可以作为事件监听器,还能转换成按钮或菜单项。

​ Action本身不是按钮或菜单项,只有把Action对象添加到容器,该容器会为Action对象创建对应的组件。

​ 传递文本是最简单的使用情况,AWT已经提供了一个****StringSelection****用于传输文本字符串。将一段文本内容,即字符串对象放进剪贴板中的步骤如下:

创建一个剪贴板实例对象Clipboard,既可以创建系统的,也可以创建本地的

创建系统的代码如下:

Clipboard board = Toolkit.getDefaultToolkit( ).getSystemClipboard( );

创建本地的代码如下:

Clipboard board = new Clipboard(“cb”); //cb代表了剪贴板的描述名称

将需要放入剪贴板中的字符串封装成StringSelection对象,代码如下:

StringSelection st = new StringSelection(“adfasdfasd”);

调用剪贴板对象中的setContents( )方法将StringSelection放进剪贴板中,该方法需要两个参数,第一个参数是Transferable对象,代表放进剪贴板中的对象;第二个参数是ClipborderOwner对象,代表剪贴板数据的所有者,通常我们无须关注剪贴板数据的所有者,所以把第二个参数设置为null

board.setContents(st,null)

从剪贴板中取出数据则比较简单,调用Clipboard对象的getData(DataFlavor flaor)方法即可取出剪贴板中指定格式的内容,如果指定flavor的数据不存在,该方法将引发UnsupportedFlavorException异常。为了避免异常的出现,可以先调用Clipboard对象的isDataFlavorAvailbale(DataFlavor flavor)来判断指定flavor的数据是否存在。代码如下:

if(board.isDataFlavorAvailble(DataFlavor.stringFlavor)){
		String content = (String)board.getData(DataFlavor.stringFlavor)
}
public class JToolBarTest {
	//1.创建相关组件
	JFrame jf = new JFrame("测试工具条");//创建窗体
	JTextArea jta = new JTextArea(6,35);//创建文本域
	JToolBar jtb = new JToolBar();//创建工具条
	JMenuBar jmb = new JMenuBar();//创建菜单栏
	JMenu edit = new JMenu("编辑");//创建菜单项
	Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard() ;//创建剪切板
	/**
	 * 2.实现复制和粘贴功能
	 * 实现相应的AbstractAction接口,可根据指定的图片创建相应的按钮
	 * 或者是转化为相应的菜单项,需要注意的是Action本身不是按钮或菜单项,
	 * 只有把Action对象添加到容器,该容器会为Action对象创建对应的组件
	 */
	Action copyAction = new AbstractAction("复制",new ImageIcon("pictures/copy.png")) {
		@Override
		public void actionPerformed(ActionEvent e) {
			/**
			 * 复制功能的实现
			 * 1.用StringSelection类型的变量接收数据
			 * 2.将接收的数据放入剪切板中
			 * 3.判断剪切板中是否存在数据,从而激活粘贴功能
			 */
			//用StringSelection类型的变量接收要实现复制的数据(文本域中被选中的数据)
			StringSelection str = new StringSelection(jta.getSelectedText());
			//将得到的数据放入剪切板clipboard中
			clipboard.setContents(str, null);
			//激活粘贴功能
			if(clipboard.isDataFlavorAvailable(DataFlavor.stringFlavor))
				//如果剪切板中有数据则激活粘贴功能
				pasteAction.setEnabled(true);
		}
	};
	Action pasteAction = new AbstractAction("粘贴",new ImageIcon("pictures/paste.png")) {
		@Override
		public void actionPerformed(ActionEvent e) {
			/**
			 * 粘贴功能的实现
			 * 1.判断剪切板中是否有内容,取出相关数据
			 * 2.在指定位置插入或者是替换相关数据
			 */		
			if(clipboard.isDataFlavorAvailable(DataFlavor.stringFlavor))
			{
				try {
					//取出剪切板中的内容
					String content = (String)clipboard.getData(DataFlavor.stringFlavor);
					//用剪切板的内容在指定的位置插入或者是替换相关的数据
					jta.replaceRange(content, jta.getSelectionStart(), jta.getSelectionEnd());
				} catch (UnsupportedFlavorException e1) {
					e1.printStackTrace();
				} catch (IOException e1) {
					e1.printStackTrace();
				}
			}
		}
	};
	public void init()
	{
		//添加相应的组件至窗体,并设置好布局
		pasteAction.setEnabled(false);//初始化为false
		//1.创建一个面板用以存放复制粘贴按钮
		JPanel jp = new JPanel();
		JButton copyBtn = new JButton(copyAction);
		jp.add(copyBtn);
		JButton pasteBtn = new JButton(pasteAction);
		jp.add(pasteBtn);
		jf.add(jp,BorderLayout.SOUTH);
		//2.将文本域添加滚动条,最后添加至窗体中
		jf.add(new JScrollPane(jta),BorderLayout.CENTER);
		//3.创建工具条并设置相关属性
		jtb.add(copyAction);
		jtb.addSeparator();
		jtb.add(pasteAction);
		jtb.setMargin(new Insets(20,10,5,30));
		jf.add(jtb,BorderLayout.NORTH);
		//4.设置菜单项
		edit.add(copyAction);
		edit.add(pasteAction);
		jmb.add(edit);
		jf.setJMenuBar(jmb);
		//5.设置窗体的相关属性
		jf.pack();
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	public static void main(String[] args) {
		new JToolBarTest().init();
	}
}

🔖JOptionPane

JOptionPane用来创建简单对话框,无序手动添加组件

JOptionPane提供了4个方法创建对话框:

showMessageDialogshowInternalMessageDialog:消息对话框,告知用户发生什么事件,类似javaScript的alert函数

showConfirmDialog与showInternalConfirmDialog:确认对话框

showInputDialogshowInternalInputDialog:输入对话框

showOptionDialogshowInternalOptionDialog:自定义对话框

public class JOptionPaneTest {
	public static void main(String[] args) {
		JFrame jf = new JFrame("对话框测试...");
		JButton jb1 = new JButton("消息确认框");
		JButton jb2 = new JButton("确认对话框");
		JButton jb3 = new JButton("输入对话框");
		JButton jb4 = new JButton("自定义对话框");
		jb1.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				/**
				 * 1.showMessageDialog与showInternalMessageDialog
				 *   消息对话框,告知用户发生什么事件,类似javaScript的alert函数
				 *   showMessageDialog(parentComponent,message,
				 *   title,messageType);
				 *   第1个参数为要显示的组件(没有特别指定则为null)
				 *   第2个参数为显示的内容
				 *   第3个参数为对话框窗体的标题
				 *   第4个参数为图标设置
				 *   	INFORMATION_MESSAGE(感叹号)
				 *   	WARNING_MESSAGE(警告)
				 *   	QUESTION_MESSAGE(询问)
				 *   	ERROR_MESSAGE(错误)
				 *   	PLAIN_MESSAGE(无)
				 */
				JOptionPane.showMessageDialog(null,"今天过得如何","标题询问",JOptionPane.PLAIN_MESSAGE);
			}
		});
		jb2.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				/**
				 * 2.showConfirmDialog与showInternalConfirmDialog
				 *   确认对话框
				 *   JOptionPane.showConfirmDialog(parentComponent, 
				 *   message, title, optionType, messageType)
				 *   第1个参数为要显示的组件(没有特别指定则为null)
				 *   第2个参数为显示的内容
				 *   第3个参数为对话框窗体的标题
				 *   第4个参数为按钮类型
				 *   	CANCEL_OPTION(确定、取消)
				 *   	CLOSED_OPTION(确定)
				 *   	DEFAULT_OPTION(默认->确定)
				 *   	OK_CANCEL_OPTION(确定、取消)
				 *   	OK_OPTION(是Y、否N)
				 *   	YES_NO_CANCEL_OPTION(是Y、否N、取消)
				 *   	YES_NO_OPTION(是Y、否N)
				 *   	YES_OPTION(是Y、否N)
				 *   第5个参数为图标设置(参考上述)
				 *   消息对话框返回的是一个整数类型的值,根据用户不同的选择,返回不同的值
				 *   从而可以借此获取用户的选择进行下一步操作
				 *   0:YES
				 *   1:NO
				 *   2:CANCEL
				 *   -1:退出对话框
				 */
				int i = JOptionPane.showConfirmDialog(null, "确认删除?","是否删除",JOptionPane.YES_NO_CANCEL_OPTION,JOptionPane.WARNING_MESSAGE);
				System.out.println("用户选择:"+i);
			}
		});
		jb3.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				/**
				 * 3.showInputDialog与showInternalInputDialog
				 *   输入对话框
				 *   JOptionPane.showInputDialog(parentComponent,message,
				 *   title,messageType,icon,selectionValues,initialSelectionValue)
				 *   第1个参数为要显示的组件(没有特别指定则为null)
				 *   第2个参数为显示的内容
				 *   第3个参数为对话框窗体的标题
				 *   第4个参数为图标设置(参考上述)
				 *   第5个参数icon设置为null
				 *   第6个参数为指定的Object类型数组(一组可供选择的文本)
				 *   第7个参数为初始化按钮选项(默认选中的文本信息)
				 */
				//输入文本信息的对话框
				String inputValue = JOptionPane.showInputDialog("请输入数据进行操作...");
				System.out.println(inputValue);
				//供用户进行选择的对话框
				Object [] possibleValue = {"博士","硕士","本科","专科"};
				Object selectedValue =JOptionPane.showInputDialog(null, "选择你的学历", "学历", JOptionPane.INFORMATION_MESSAGE, null, possibleValue, possibleValue[0]);
				System.out.println(selectedValue);
				
			}
		});
		jb4.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				/**
				 *  4.showOptionDialog与showInternalOptionDialog
				 *    自定义对话框
				 *    可以自定义实现按钮设置(将要设置的按钮存储在一个Object类型的数组中)
				 *    showOptionDialog(parentComponent, message, title,
				 *     optionType, messageType, icon, options, initialValue)
				 *   第1个参数为要显示的组件(没有特别指定则为null)
				 *   第2个参数为显示的内容
				 *   第3个参数为对话框窗体的标题
				 *   第4个参数为按钮类型(参考上述)
				 *   第5个参数为图标设置(参考上述)
				 *   第6个参数icon设置为null
				 *   第7个参数为指定的Object类型数组(一组按钮文本)
				 *   第8个参数为初始化按钮选项(默认选中的按钮)
				 */
				Object[] options = {"等待","继续","取消"};
				int i = JOptionPane.showOptionDialog(null,"是否继续往下执行?", "提示",JOptionPane.DEFAULT_OPTION,JOptionPane.INFORMATION_MESSAGE, null, options, options[1]);
				System.out.println("用户选择:"+i);
			}
		});
		JPanel jp = new JPanel();
		jp.add(jb1);
		jp.add(jb2);
		jp.add(jb3);
		jp.add(jb4);
		jf.add(jp);
		jf.pack();
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
}

🔖JSplitPane

​ JSplitPane是一个容器,用来创建一个分割面板,将一个容器分成两个部分,提供一个分隔条,可以通过拖动调整分隔条。

JSplitPane js = new JSplitPane(方向 ,| 上组件,右 | 下组件);

另外可以创建一个newContinusLayout指定分割面板是否支持连续布局

public class JSplitPaneTest {
	JFrame jf = new JFrame("JSplitPane测试");
	//分别创建三个面板、三个按钮
	JPanel jp1 = new JPanel();
	JPanel jp2 = new JPanel();
	JPanel jp3 = new JPanel();
	JButton bt1 = new JButton("按钮1");
	JButton bt2 = new JButton("按钮2");
	JButton bt3 =new JButton("按钮3");
	public void init()
	{
		//1.设置三个合适大小的画板JPanel,再将三个按钮分别加入其中
		jp1.setPreferredSize(new Dimension(150,300));
		jp2.setPreferredSize(new Dimension(300,150));
		jp3.setPreferredSize(new Dimension(300,1500));
		jp1.add(bt1);
		jp2.add(bt2);
		jp3.add(bt3);
		/**
		 * 2.创建分隔布局,分别创建一个垂直分割和水平分割
		 * JSplitPane js = new JSplitPane(方向 , 左 | 上组件,右 | 下组件);
		 * 另外可以创建一个newContinusLayout指定分割面板是否支持连续布局
		 */
		JSplitPane left =new JSplitPane(JSplitPane.VERTICAL_SPLIT, true, jp1, new JScrollPane(jp2));
		left.setOneTouchExpandable(true);//一触即展设置为true
		JSplitPane content = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT,true,left,new JScrollPane(jp3));
		content.setOneTouchExpandable(false);//一触即展设置为false
		jf.add(content);
		//3.设置窗体的相关属性
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	public static void main(String[] args) {
		new JSplitPaneTest().init();
	}
}

🔖JProgressBar(进度条)

​ 使用JProgressBar(进度条)、ProgressMonitor(进度对话框),进度条是图形界面中广泛使用的GUI组件,当复制一个较大的文件时,操作系统会显示一个进度条,用于标识复制操作完成的比例。

​ 使用JProgressBar创建进度条,有如下步骤

​ 创建一个JProgressBar对象,创建该对象时可以指定3个参数,用于设置进度条的排列方向,进度条的最大值和最小值。也可以在创建该对象完成后,单独进行设置这3个属性。

JProgressBar bar = new JProgressBar(JPorgressBar.VERTICAL);

​ 调用该对象的常用方法设置进度条的普通属性。JProgressBar除了提供设置排列方向、最大值、最小值的setter和getter方法之外,还提供了如下方法:

setBorderPainted(boolean b):设置该进度条是否使用边框。

setIndeterminate(boolean newValue):设置该进度条是否是进度不精确的进度条,如果指定一个进度条的精度不正确,将看到一个滑块在进度条中左右移动。

setStringPainted(boolean newValue):设置是否在此进度条中显示完成百分比。

setValue():当进度发生更改时调用此方法重新设值。

double getPercentComplete():返回进度条的完成百分比

String getString():返回进度字符串的当前值

public class JProgressBarTest {
	public JFrame jf = new JFrame("进度条测试...");
	public JProgressBar bar = new JProgressBar(JProgressBar.HORIZONTAL);
	public JCheckBox indeterinate =new JCheckBox("不确定的进度条");
	public JCheckBox noBorder =new JCheckBox("不绘制边框的进度条");
	public void init()
	{
		/**
		 * 设置进度条的相关属性
		 * 1.设置进度条的最大值setMaximum和最小值setMinimum
		 * 2.确定进度条是否应该呈现进度字符串setStringPainted
		 * 3.确定进度条是否绘制其边框setBorderPainted
		 * 4.确定进度条处于确定模式中还是处于不确定模式中setIndeterminate
		 */
		bar.setMaximum(100);
		bar.setMinimum(0);
		bar.setStringPainted(true);
		bar.setBorderPainted(true);
		noBorder.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent arg0) {
				bar.setBorderPainted(!noBorder.isSelected());
			}
		});
		indeterinate.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(ActionEvent e) {
				bar.setIndeterminate(indeterinate.isSelected());
				bar.setStringPainted(!indeterinate.isSelected());
			}
		});
		jf.add(bar,BorderLayout.NORTH);
		jf.add(indeterinate,BorderLayout.CENTER);
		jf.add(noBorder,BorderLayout.SOUTH);
		jf.pack();
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		//模拟任务的进度
		for(int i=0;i<100;i++) {
			bar.setValue(i+1);
			try {
				Thread.sleep(100); //休眠 100毫秒
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
	}
	public static void main(String[] args) {
		new JProgressBarTest().init();
	}
}

🔖JTabbedPane

​ JTabbledPane在窗口上创建多个标签页,每个标签页可以控制一个与外部窗口相同大小的容器

public class TestJTabbedPane {
	JFrame jf = new JFrame("测试Tab页面");
	//创建5个面板用以测试
	JPanel jp1 = new JPanel();
	JPanel jp2 = new JPanel();
	JPanel jp3 = new JPanel();
	JPanel jp4 = new JPanel();
	JPanel jp5 = new JPanel();
	/**
	 * 创建一个Tab页面的标签放在左边,
	 * 采用换行布局策略的JTabbedPane
	 * JTabbedPane(JTabbedPane.LEFT,JTabbedPane.WRAP_TAB_LAYOUT);
	 * 第1个参数是指将JTabbedPane面板放在左边
	 * 第2个参数是指采用的是换行布局策略
	 */
	JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.LEFT,JTabbedPane.WRAP_TAB_LAYOUT);
	//定义单选按钮组
	String[] layouts = { "换行布局", "滚动条布局" };
	String[] positions = { "左边", "顶部", "右边", "底部" };
	//初始化组件布局
	public void init() {
		String tip = "测试提示";
		/**
		 * 向JTabbedPane中添加5个Tab页面,并指定了标题、图标和提示
		 * 但该Tab页面的组件为null
		 */
		tabbedPane.addTab("jp1", null, null, tip);
		tabbedPane.addTab("jp2", null, null, tip);
		tabbedPane.addTab("jp3", null, null, tip);
		tabbedPane.addTab("jp4", null, null, tip);
		tabbedPane.addTab("jp5", null, null, tip);
		//将tabbedPane加载到窗体
		jf.add(tabbedPane, BorderLayout.CENTER);
		//为JTabbedPane添加事件监听器
		tabbedPane.addChangeListener(new ChangeListener() {
			public void stateChanged(ChangeEvent event) {
				//如果被选择的组件依然是空
				if (tabbedPane.getSelectedComponent() == null) {
					//获取所选Tab页
					int n = tabbedPane.getSelectedIndex();
					//为指定标前页加载内容
					loadTab(n);
				}
			}
		});
		//调用自定义的方法loadTab系统默认选择第一页,加载第一页内容
		loadTab(0);
		tabbedPane.setPreferredSize(new Dimension(500, 300));
		//增加控制标签布局、标签位置的单选按钮
		JPanel buttonPanel = new JPanel();
		ChangeAction action = new ChangeAction();
		buttonPanel.add(new ButtonPanel(action, "选择标签布局策略", layouts));
		buttonPanel.add(new ButtonPanel(action, "选择标签位置", positions));
		jf.add(buttonPanel, BorderLayout.SOUTH);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		jf.pack();
		jf.setVisible(true);
	}
	//为指定标签页加载内容
	private void loadTab(int n) {
		String title = tabbedPane.getTitleAt(n);
		tabbedPane.setComponentAt(n, new JLabel("我是第个" + n + "面板"));
		//根据标签页的标题获取对应图书封面
	}
	// 定义改变标签页的布局策略,放置位置的监听器
	class ChangeAction implements ActionListener {
		public void actionPerformed(ActionEvent event) {
			JRadioButton source = (JRadioButton) event.getSource();
			String selection = source.getActionCommand();
			if (selection.equals(layouts[0])) {
				tabbedPane.setTabLayoutPolicy(JTabbedPane.WRAP_TAB_LAYOUT);
			} else if (selection.equals(layouts[1])) {
				tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);
			} else if (selection.equals(positions[0])) {
				tabbedPane.setTabPlacement(JTabbedPane.LEFT);
			} else if (selection.equals(positions[1])) {
				tabbedPane.setTabPlacement(JTabbedPane.TOP);
			} else if (selection.equals(positions[2])) {
				tabbedPane.setTabPlacement(JTabbedPane.RIGHT);
			} else if (selection.equals(positions[3])) {
				tabbedPane.setTabPlacement(JTabbedPane.BOTTOM);
			}
		}
	}
	public static void main(String[] args) {
		new TestJTabbedPane().init();
	}
}
// 定义一个JPanel类扩展类,该类的对象包含多个纵向排列的JRadioButton控件
// 且Panel扩展类可以指定一个字符串作为TitledBorder
class ButtonPanel extends JPanel {
	private ButtonGroup group;
	public ButtonPanel(TestJTabbedPane.ChangeAction action, String title,
			String[] labels) {
		setBorder(BorderFactory.createTitledBorder(
				BorderFactory.createEtchedBorder(), title));
		setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
		group = new ButtonGroup();
		for (int i = 0; labels != null && i < labels.length; i++) {
			JRadioButton b = new JRadioButton(labels[i]);
			b.setActionCommand(labels[i]);
			add(b);
			// 添加事件监听器
			b.addActionListener(action);
			group.add(b);
			b.setSelected(i == 0);
		}
	}
}

🔖JDesktopPane和JInternalFrame

​ JdesktopOane和JinternalFrame需要结合使用,JdesktopPane代表一个虚拟桌面,JinternalFrame用于创建内部窗口。

使用步骤:

  • 创建一个JdesktopPane对象,直接new即可

  • 使用JinternalFrame创建一个内部窗口,创建内部窗口与创建Jframe窗口有一些区别,创建JinternalFrame对象的时候,可以传入一个字符串作为内部窗口的标题,还可以传入4个boolean值,来确定内部窗口是否允许改变窗口大小,关闭窗口,最小化窗口和最大化窗口

  • 获得了内部窗口以后,这个窗口的用法和普通窗口的用法基本的相似,一样可以指定窗口的布局管理器,向窗口内添加组件

  • 将内部窗口以合适的大小放在合适的位置显示出来。注意坐标是位于虚拟桌面的

  • 将内部窗口添加到JdesktopPane容器,再将JdesktopPane添加到其他容器

public class TestInternalFrame {
	final int DESKTOP_WIDTH = 480;
	final int DESKTOP_HEIGHT = 360;
	final int FRAME_DISTANCE = 30;
	JFrame jf = new JFrame("父窗口");
	//定义一个虚拟桌面
	private JDesktopPane desktop = new JDesktopPane();
	//保存下一个内部窗口的座标点
	private int nextFrameX;
	private int nextFrameY;
	//定义内部窗口为虚拟桌面的1/2大小
	private int width = DESKTOP_WIDTH / 2;
	private int height = DESKTOP_HEIGHT / 2;
	//为主窗口定义2个菜单
	JMenu fileMenu = new JMenu("文件");
	//定义newAction用于创建菜单和工具按钮
	Action newAction = new AbstractAction("新建", new ImageIcon("pictures/copy.png")) {
		public void actionPerformed(ActionEvent event) {
			//创建内部窗口
			final JInternalFrame iframe = new JInternalFrame("新文档", true, // 可改变大小
					true, //可关闭
					true, //可最大化
					true); //可最小化
			iframe.add(new JScrollPane(new JTextArea(8, 40)));
			//将内部窗口添加到虚拟桌面中
			desktop.add(iframe);
			//设置内部窗口的原始位置(内部窗口默认大小是0X0,放在0,0位置)
			iframe.reshape(nextFrameX, nextFrameY, width, height);
			//使该窗口可见,并尝试选中它
			iframe.show();
			//计算下一个内部窗口的位置
			nextFrameX += FRAME_DISTANCE;
			nextFrameY += FRAME_DISTANCE;
			if (nextFrameX + width > desktop.getWidth())
				nextFrameX = 0;
			if (nextFrameY + height > desktop.getHeight())
				nextFrameY = 0;
		}
	};
	//定义exitAction用于创建菜单和工具按钮
	Action exitAction = new AbstractAction("退出", new ImageIcon("pictures/paste.png")) {
		public void actionPerformed(ActionEvent event) {
			System.exit(0);
		}
	};
	public void init() {
		//为窗口安装菜单条和工具条
		JMenuBar menuBar = new JMenuBar();
		JToolBar toolBar = new JToolBar();
		jf.setJMenuBar(menuBar);
		menuBar.add(fileMenu);
		fileMenu.add(newAction);
		fileMenu.add(exitAction);
		toolBar.add(newAction);
		toolBar.add(exitAction);
		desktop.setPreferredSize(new Dimension(480, 360));
		// 将虚拟桌面添加到顶级JFrame容器中
		jf.add(desktop);
		jf.add(toolBar, BorderLayout.NORTH);
		try {
			/**
			 * 设置内部窗口外形,使其像windows风格的窗口,从而
			 * 美化窗体,否则默认的是unix类型风格
			 */
			UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
		} catch (ClassNotFoundException | InstantiationException
				| IllegalAccessException | UnsupportedLookAndFeelException e) {
			e.printStackTrace();
		}
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		jf.pack();
		jf.setVisible(true);
	}
	public static void main(String[] args) {
		new TestInternalFrame().init();
	}
}

🔖JTree

(1)JTree基本

树是一个非常常用的组件,例如windows资源管理器。

  • 根节点和普通节点。

  • 一个树只能有一个根节点。

  • JTree和TreeModel可以开发树。

注意:不光swing中有树,在web开发中树叶是常用的组件,但是swing中的树在web中是不能用的,但是原理相同,这里需要好好学习

树有两种状态,展开状态和折叠状态

public class JTreeTest {
	private JFrame jf = new JFrame("简单树测试...");
	private JTree tree ;
	//1.定义相关的树的节点信息
	DefaultMutableTreeNode root = new DefaultMutableTreeNode("中国");
	
	DefaultMutableTreeNode guangdong = new DefaultMutableTreeNode("广东");
	DefaultMutableTreeNode zhejiang = new DefaultMutableTreeNode("浙江");
	DefaultMutableTreeNode shandong = new DefaultMutableTreeNode("山东");
	
	DefaultMutableTreeNode shenzhen = new DefaultMutableTreeNode("深圳");
	DefaultMutableTreeNode guangzhou = new DefaultMutableTreeNode("广州");
	
	DefaultMutableTreeNode hangzhou = new DefaultMutableTreeNode("杭州");
	DefaultMutableTreeNode taizhou = new DefaultMutableTreeNode("台州");
	
	DefaultMutableTreeNode qingdao = new DefaultMutableTreeNode("青岛");
	
	public void init()
	{
		tree = new JTree(root);
		//2.设置节点布局
		guangdong.add(shenzhen);
		guangdong.add(guangzhou);
		zhejiang.add(hangzhou);
		zhejiang.add(taizhou);
		shandong.add(qingdao);
		//将相应节点加到根节点root下
		root.add(guangdong);
		root.add(zhejiang);
		root.add(shandong);
		//设置是否显示根节点(默认是false)
		tree.setRootVisible(true);
		//3.将树显示在窗体上,并设置窗体的相关属性
		jf.add(tree);
		jf.pack();
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	public static void main(String[] args) {
		new JTreeTest().init();
	}
}

参考示例:

构建拖动并且能够编辑的树,默认JTree生成的树是不能编辑的。使用setEditable方法就可以

对树的编辑 一般有添加兄弟节点、子节点、删除节点、编辑节点、节点拖动这些功能

TreePath类记录了从根节点到指定节点的所有节点,TreePath由一系列节点组成

如果要获取JTree中被选定的节点,TreePath或者TreeNode

TreePath path = tree.getSelectionPath();
TreeNode target = (TreeNode)path.getLastPathComponent();

TreeNode target = (TreeNode)tree.getLastSelectedPathComponent();

​ 只要获取选中的节点,就可以通过DefaultTreeModel提供的方法来编辑节点。另外,也可以直接通过TreeNode提供方法来添加、删除和修改节点,但是通过这种方式作出的修改,必须手工调用JTree的updateUI通知JTree重绘所有节点

如果是拖动节点如何知道拖到哪里去?需要得到鼠标移动的x和y

TreePath path = tree.getPathForLocation(e.getX(),e.getY());
import javax.swing.*;
import javax.swing.tree.*;
import java.awt.event.*;
import java.awt.*;

public class EditJTree {
	JFrame jf;
	JTree tree;
	// 上面JTree对象对应的model
	DefaultTreeModel model;

	// 定义几个初始节点
	DefaultMutableTreeNode root = new DefaultMutableTreeNode("中国");
	DefaultMutableTreeNode shandong = new DefaultMutableTreeNode("山东");
	DefaultMutableTreeNode jiangsu = new DefaultMutableTreeNode("江苏");
	DefaultMutableTreeNode jinan = new DefaultMutableTreeNode("济南");
	DefaultMutableTreeNode qingdao = new DefaultMutableTreeNode("青岛");
	DefaultMutableTreeNode nanjing = new DefaultMutableTreeNode("南京");
	DefaultMutableTreeNode suzhou = new DefaultMutableTreeNode("苏州");

	// 定义需要被拖动的TreePath
	TreePath movePath;

	JButton addSiblingButton = new JButton("添加兄弟节点");
	JButton addChildButton = new JButton("添加子节点");
	JButton deleteButton = new JButton("删除节点");
	JButton editButton = new JButton("编辑当前节点");

	public void init() {
		shandong.add(jinan);
		shandong.add(qingdao);
		jiangsu.add(nanjing);
		jiangsu.add(suzhou);
		root.add(shandong);
		root.add(jiangsu);

		jf = new JFrame("树");
		tree = new JTree(root);
		// 获取JTree对应的TreeModel对象
		model = (DefaultTreeModel) tree.getModel();
		// 设置JTree可编辑
		tree.setEditable(true);
		MouseListener ml = new MouseAdapter() {
			// 按下鼠标时候获得被拖动的节点
			public void mousePressed(MouseEvent e) {
				// 如果需要唯一确定某个节点,必须通过TreePath来获取。
				TreePath tp = tree.getPathForLocation(e.getX(), e.getY());
				if (tp != null) {
					movePath = tp;
				}
			}

			// 鼠标松开时获得需要拖到哪个父节点
			public void mouseReleased(MouseEvent e) {
				// 根据鼠标松开时的TreePath来获取TreePath
				TreePath tp = tree.getPathForLocation(e.getX(), e.getY());

				if (tp != null && movePath != null) {
					// 阻止向子节点拖动
					if (movePath.isDescendant(tp) && movePath != tp) {
						JOptionPane.showMessageDialog(jf,
								"目标节点是被移动节点的子节点,无法移动!", "非法操作",
								JOptionPane.ERROR_MESSAGE);
						return;
					}
					// 既不是向子节点移动,而且鼠标按下、松开的不是同一个节点
					else if (movePath != tp) {
						System.out.println(tp.getLastPathComponent());
						// add方法可以先将原节点从原父节点删除,再添加到新父节点中
						((DefaultMutableTreeNode) tp.getLastPathComponent())
								.add((DefaultMutableTreeNode) movePath
										.getLastPathComponent());
						movePath = null;
						tree.updateUI();
					}
				}
			}
		};
		tree.addMouseListener(ml);

		JPanel panel = new JPanel();

		addSiblingButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				// 获取选中节点
				DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree
						.getLastSelectedPathComponent();
				// 如果节点为空,直接返回
				if (selectedNode == null)
					return;
				// 获取该选中节点的父节点
				DefaultMutableTreeNode parent = (DefaultMutableTreeNode) selectedNode
						.getParent();
				// 如果父节点为空,直接返回
				if (parent == null)
					return;
				// 创建一个新节点
				DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(
						"新节点");
				// 获取选中节点的选中索引
				int selectedIndex = parent.getIndex(selectedNode);
				// 在选中位置插入新节点
				model.insertNodeInto(newNode, parent, selectedIndex + 1);
				// --------下面代码实现显示新节点(自动展开父节点)-------
				// 获取从根节点到新节点的所有节点
				TreeNode[] nodes = model.getPathToRoot(newNode);
				// 使用指定的节点数组来创建TreePath
				TreePath path = new TreePath(nodes);
				// 显示指定TreePath
				tree.scrollPathToVisible(path);
			}
		});
		panel.add(addSiblingButton);
		addChildButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				// 获取选中节点
				DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree
						.getLastSelectedPathComponent();
				// 如果节点为空,直接返回
				if (selectedNode == null)
					return;
				// 创建一个新节点
				DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(
						"新节点");
				// 直接通过model来添加新节点,则无需通过调用JTree的updateUI方法
				// model.insertNodeInto(newNode, selectedNode,
				// selectedNode.getChildCount());
				// 直接通过节点添加新节点,则需要调用tree的updateUI方法
				selectedNode.add(newNode);
				// --------下面代码实现显示新节点(自动展开父节点)-------
				TreeNode[] nodes = model.getPathToRoot(newNode);
				TreePath path = new TreePath(nodes);
				tree.scrollPathToVisible(path);
				tree.updateUI();
			}
		});
		panel.add(addChildButton);
		deleteButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				DefaultMutableTreeNode selectedNode = (DefaultMutableTreeNode) tree
						.getLastSelectedPathComponent();
				if (selectedNode != null && selectedNode.getParent() != null) {
					// 删除指定节点
					model.removeNodeFromParent(selectedNode);
				}
			}
		});
		panel.add(deleteButton);
		editButton.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent event) {
				TreePath selectedPath = tree.getSelectionPath();
				if (selectedPath != null) {
					// 编辑选中节点
					tree.startEditingAtPath(selectedPath);
				}
			}
		});
		panel.add(editButton);

		jf.add(new JScrollPane(tree));
		jf.add(panel, BorderLayout.SOUTH);
		jf.pack();
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		jf.setVisible(true);
	}

	public static void main(String[] args) {
		new EditJTree().init();
	}
}
(2)s树节点监听器
public class JTreeListener {
	private JFrame jf = new JFrame("树节点监听器测试...");
	private JTextArea content = new JTextArea(400,500);
	private JTree tree ;
	//1.定义相关的树的节点信息
	DefaultMutableTreeNode root = new DefaultMutableTreeNode("中国");
	
	DefaultMutableTreeNode guangdong = new DefaultMutableTreeNode("广东");
	DefaultMutableTreeNode zhejiang = new DefaultMutableTreeNode("浙江");
	DefaultMutableTreeNode shandong = new DefaultMutableTreeNode("山东");
	
	DefaultMutableTreeNode shenzhen = new DefaultMutableTreeNode("深圳");
	DefaultMutableTreeNode guangzhou = new DefaultMutableTreeNode("广州");
	
	DefaultMutableTreeNode hangzhou = new DefaultMutableTreeNode("杭州");
	DefaultMutableTreeNode taizhou = new DefaultMutableTreeNode("台州");
	
	DefaultMutableTreeNode qingdao = new DefaultMutableTreeNode("青岛");
	
	public void init()
	{
		tree = new JTree(root);
		//2.设置节点布局
		guangdong.add(shenzhen);
		guangdong.add(guangzhou);
		zhejiang.add(hangzhou);
		zhejiang.add(taizhou);
		shandong.add(qingdao);
		//将相应节点加到根节点root下
		root.add(guangdong);
		root.add(zhejiang);
		root.add(shandong);
		//设置是否显示根节点(默认是false)
		tree.setRootVisible(true);
		
		/**
		 * 树节点监听器测试
		 */
		//设置一次只能够选择一个节点信息
	tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
		//添加树节点监听器TreeSelectionListener
		tree.addTreeSelectionListener(new TreeSelectionListener() {
			@Override
			public void valueChanged(TreeSelectionEvent e) {
				if(e.getOldLeadSelectionPath()!=null)
					content.append("之前选中的路径为:"+e.getOldLeadSelectionPath()+"\r\n");
					content.append("现在选择的节点路径为"+e.getNewLeadSelectionPath()+"\r\n");
			}
		});
		//将树显示在窗体上,并设置窗体的相关属性
		jf.add(tree,BorderLayout.WEST);
		jf.add(content);
		jf.setBounds(500, 200, 500, 500);
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	public static void main(String[] args) {
		new JTreeListener().init();
	}
}
(3)改变树的外观
public class JTreeTest2 {
	/**
	 * 用指定的图标改变树的外观
	 */
	private JFrame jf = new JFrame("简单树测试...");
	private JTree tree ;
	//1.定义相关的树的节点信息
	DefaultMutableTreeNode root = new DefaultMutableTreeNode("中国");
	
	DefaultMutableTreeNode guangdong = new DefaultMutableTreeNode("广东");
	DefaultMutableTreeNode zhejiang = new DefaultMutableTreeNode("浙江");
	DefaultMutableTreeNode shandong = new DefaultMutableTreeNode("山东");
	
	DefaultMutableTreeNode shenzhen = new DefaultMutableTreeNode("深圳");
	DefaultMutableTreeNode guangzhou = new DefaultMutableTreeNode("广州");
	
	DefaultMutableTreeNode hangzhou = new DefaultMutableTreeNode("杭州");
	DefaultMutableTreeNode taizhou = new DefaultMutableTreeNode("台州");
	
	DefaultMutableTreeNode qingdao = new DefaultMutableTreeNode("青岛");
	
	public void init()
	{
		tree = new JTree(root);
		//2.设置节点布局
		guangdong.add(shenzhen);
		guangdong.add(guangzhou);
		zhejiang.add(hangzhou);
		zhejiang.add(taizhou);
		shandong.add(qingdao);
		//将相应节点加到根节点root下
		root.add(guangdong);
		root.add(zhejiang);
		root.add(shandong);
		//设置是否显示根节点(默认是false)
		tree.setRootVisible(true);
		/**
		 * 3.设置树节点的相关属性:
		 * 通过创建DefaultTreeCellRenderer对象进行操作
		 * 对选中和未被选中的节点背景色作区分:
		 * 	setBackgroundSelectionColor(Color c)
		 * 	setBackgroundNonSelectionColor(Color c)
		 * 设置边框的颜色:
		 * 	setBorderSelectionColor(Color c)
		 * 设置节点图标:
		 * 	setLeafIcon(ImageIcon icon):设置叶子节点图标
		 * 	setOpenIcon(ImageIcon icon):设置展开节点图标
		 * 	setClosedIcon(ImageIcon icon):设置闭合节点图标
		 * 设置文本的颜色(区分选中和未被选中的节点):
		 * 	setTextSelectionColor(Color c)
		 * 	setTextNonSelectionColor(Color c)
		 * 完成相关属性的设置,最后通过JTree的对象调用setCellRenderer
		 * 方法完成设置
		 */
		DefaultTreeCellRenderer dtr = new DefaultTreeCellRenderer();
		//设置节点背景色
		dtr.setBackgroundSelectionColor(new Color(220,220,220));
		dtr.setBackgroundNonSelectionColor(new Color(140,150,160));
		//设置边框颜色
		dtr.setBorderSelectionColor(Color.red);
		//设置节点图标
		dtr.setLeafIcon(new ImageIcon("pictures/city.png"));
		dtr.setOpenIcon(new ImageIcon("pictures/province.png"));
		dtr.setClosedIcon(new ImageIcon("pictures/country.png"));
		//设置文本的颜色
		dtr.setTextSelectionColor(new Color(255,250,125));
		dtr.setTextNonSelectionColor(new Color(0,100,200));
		//调用setCellRenderer方法将DefaultTreeCellRenderer对象传递进来
		tree.setCellRenderer(dtr);
		//将树显示在窗体上,并设置窗体的相关属性
		jf.add(tree);
		jf.pack();
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	public static void main(String[] args) {
		new JTreeTest2().init();
	}
}

案例分析:

public class ExtendsTreeRenderer {
	JFrame jf = new JFrame("根据节点类型定义图标");

	JTree tree;
	DefaultMutableTreeNode root = new DefaultMutableTreeNode(new Node(
			DBType.ROOT, "数据库导航"));
	DefaultMutableTreeNode salaryDb = new DefaultMutableTreeNode(new Node(
			DBType.DATABASE, "公司工资数据库"));
	DefaultMutableTreeNode customerDb = new DefaultMutableTreeNode(new Node(
			DBType.DATABASE, "公司客户数据库"));
	// 定义salaryDb的两个子节点
	DefaultMutableTreeNode employee = new DefaultMutableTreeNode(new Node(
			DBType.TABLE, "员工表"));
	DefaultMutableTreeNode attend = new DefaultMutableTreeNode(new Node(
			DBType.TABLE, "考勤表"));
	// 定义customerDb的一个子节点
	DefaultMutableTreeNode contact = new DefaultMutableTreeNode(new Node(
			DBType.TABLE, "联系方式表"));

	// 定义employee的三个子节点
	DefaultMutableTreeNode id = new DefaultMutableTreeNode(new Node(
			DBType.INDEX, "员工ID"));
	DefaultMutableTreeNode name = new DefaultMutableTreeNode(new Node(
			DBType.COLUMN, "姓名"));
	DefaultMutableTreeNode gender = new DefaultMutableTreeNode(new Node(
			DBType.COLUMN, "性别"));

	public void init() throws Exception {
		root.add(salaryDb);
		root.add(customerDb);
		salaryDb.add(employee);
		salaryDb.add(attend);
		customerDb.add(contact);
		employee.add(id);
		employee.add(name);
		employee.add(gender);
		// 以根节点创建树
		tree = new JTree(root);

		tree.setShowsRootHandles(true);
		tree.setRootVisible(true);
		tree.setCellRenderer(new MyRender());
		// 设置使用Windows风格外观
		UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
		// 更新JTree的UI外观
		SwingUtilities.updateComponentTreeUI(tree);
		jf.add(new JScrollPane(tree));
		jf.pack();
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		jf.setVisible(true);
	}

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

class MyRender extends DefaultTreeCellRenderer {
	// 初始化5个图标
	ImageIcon rootIcon = new ImageIcon("icon/root.png");
	ImageIcon databaseIcon = new ImageIcon("icon/database.png");
	ImageIcon tableIcon = new ImageIcon("icon/table.png");
	ImageIcon columnIcon = new ImageIcon("icon/column.png");
	ImageIcon indexIcon = new ImageIcon("icon/index.png");

	@Override
	public Component getTreeCellRendererComponent(JTree tree, Object value,
			boolean sel, boolean expanded, boolean leaf, int row,
			boolean hasFocus) {
		// TODO Auto-generated method stub
		// 执行父类默认的节点绘制操作
		super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf,
				row, hasFocus);
		DefaultMutableTreeNode node = (DefaultMutableTreeNode) value;
		Node data = (Node) node.getUserObject();
		// 根据数据节点里的nodeType数据决定节点图标
		ImageIcon icon = null;
		switch (data.nodeType) {
		case DBType.ROOT:
			icon = rootIcon;
			break;
		case DBType.DATABASE:
			icon = databaseIcon;
			break;
		case DBType.TABLE:
			icon = tableIcon;
			break;
		case DBType.COLUMN:
			icon = columnIcon;
			break;
		case DBType.INDEX:
			icon = indexIcon;
			break;
		}
		// 改变图标
		this.setIcon(icon);
		return this;
	}
}
class Node {
	public int nodeType;
	public String nodeData;
	public Node(int nodeType, String nodeData) {
		this.nodeType = nodeType;
		this.nodeData = nodeData;
	}
	public String toString() {
		return nodeData;
	}
}
interface DBType {
	int ROOT = 0;
	int DATABASE = 1;
	int TABLE = 2;
	int COLUMN = 3;
	int INDEX = 4;
}

🔖JTable

​ 表格技术更是GUI程序中常用的组件

public class JTableTest {
	private JFrame jf = new JFrame("简单的JTable测试");
	private JTable table;
	//1.定义一个Object类型的数据作为表格数据(一般可以从数据库中读取进行拼接)
	Object[][] data = {new Object[]{"haha","男",18},new Object[]{"xixi","女",20},new Object[]{"bibi","女",19}};
	//2.定义一个一维数组作为表头
	Object[] title = {"姓名","性别","年龄"};
	public void init()
	{
		//初始化表格:new JTable(表的数据,表头);
		table = new JTable(data,title);
		jf.add(new JScrollPane(table));
		jf.pack();
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	public static void main(String[] args) {
		new JTableTest().init();
	}
}
TableModel、TableColumnModel和监听器
  • JTable使用TableModel来保存表格中的所有状态数据,不强制保存表格显示的数据

  • AbstractTableModel抽象类实现了TableModel接口里的大部分方法,只需要实现

    getColumnCount、getRowCount、getValueAt三个方法即可

    JTable使用TableColumnModel来保存表格所有数据列的状态数据,TableColumnModel提供增加删除和移动数据列

//表格监听
public class JTableListener {
	private JFrame jf = new JFrame("简单的JTable测试");
	private JTable table;
	private MyTableModle model;
	public void init()
	{
		//初始化表格:new JTable(表的数据,表头);
		/**
		 * JTable使用TableModel来保存表格中的所有状态数据
		 * 不强制保存表格显示的数据
		 */
		model = new MyTableModle();
		model.addTableModelListener(new TableModelListener() {
			
			@Override
			public void tableChanged(TableModelEvent e) {
				System.out.println(e.getFirstRow()+"-----");
				System.out.println(e.getColumn()+"----");
				System.out.println(model.getValueAt(e.getFirstRow(), e.getColumn()));
			}
		});
//		table = new JTable(data,title);
		table = new JTable(model);
		jf.add(new JScrollPane(table));
		jf.pack();
		jf.setVisible(true);
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	class MyTableModle extends AbstractTableModel{

		//1.定义一个Object类型的数据作为表格数据(一般可以从数据库中读取进行拼接)
		Object[][] data = {new Object[]{"haha","男",18},new Object[]{"xixi","女",20},new Object[]{"bibi","女",19}};
		//2.定义一个一维数组作为表头
		Object[] title = {"姓名","性别","年龄"};
		
		@Override
		public int getColumnCount() {
			return title.length;
		}

		@Override
		public int getRowCount() {
			return data.length;
		}

		@Override
		public Object getValueAt(int rowIndex, int columIndex) {
			return data[rowIndex][columIndex];
		}
		
		@Override
		public String getColumnName(int column) {
			return (String) title[column];
		}
		
		@Override
		public boolean isCellEditable(int rowIndex, int columnIndex) {
			return true;
		}
		
		@Override
		public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
			//监控改变的行和列
			System.out.println(aValue);
			System.out.println(rowIndex);
			System.out.println(columnIndex);
			//改变值
			data[rowIndex][columnIndex]=aValue;
			//更新表格
			fireTableCellUpdated(rowIndex, columnIndex);
		}
	}
	public static void main(String[] args) {
		new JTableListener().init();
	}
}
参考示例
参考示例1:JTable表格排序
import java.util.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;

public class SortTable {
	JFrame jf = new JFrame("简单表格");
	// 定义二维数组作为表格数据
	Object[][] tableData = { new Object[] { "胡伟红", 29, "女" },
			new Object[] { "盖茨", 56, "男" }, new Object[] { "Scott", 35, "男" },
			new Object[] { "王菲", 38, "女" }, new Object[] { "Baby", 2, "男" } };
	// 定义一维数据作为列标题
	Object[] columnTitle = { "姓名", "年龄", "性别" };

	// 以二维数组和一维数组来创建一个JTable对象
	JTable table = new JTable(tableData, columnTitle);

	// 将原表格里的model包装成新的SortFilterModel对象
	SortableTableModel sorterModel = new SortableTableModel(table.getModel());

	public void init() {
		table.setModel(sorterModel);

		table.getTableHeader().addMouseListener(new MouseAdapter() {
			public void mouseClicked(MouseEvent event) {
				// 如果单击次数小于2,即不是双击,直接返回
				if (event.getClickCount() < 2) {
					return;
				}
				// 找出鼠标双击事件所在的列索引
				int tableColumn = table.columnAtPoint(event.getPoint());
				// 将JTable中的列索引转换成对应TableModel中的列索引
				int modelColumn = table.convertColumnIndexToModel(tableColumn);
				// 根据指定列进行排序
				sorterModel.sort(modelColumn);
			}
		});

		// 将JTable对象放在JScrollPane中,并将该JScrollPane放在窗口中显示出来
		jf.add(new JScrollPane(table));
		jf.pack();
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		jf.setVisible(true);
	}

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

class SortableTableModel extends AbstractTableModel {
	private TableModel model;
	private int sortColumn;
	private Row[] rows;

	// 将一个已经存在TableModel对象包装成SortableTableModel对象
	public SortableTableModel(TableModel m) {
		// 将被封装的TableModel传入
		model = m;
		rows = new Row[model.getRowCount()];
		// 将原TableModel中的每行记录的索引使用Row数组保存起来
		for (int i = 0; i < rows.length; i++) {
			rows[i] = new Row(i);
		}
	}

	// 实现根据指定列进行排序
	public void sort(int c) {
		sortColumn = c;
		Arrays.sort(rows);
		fireTableDataChanged();
	}

	// 下面三个方法需要访问Model中的数据,所以涉及到本Model中数据
	// 和被包装Model数据中的索引转换,程序使用rows数组完成这种转换。
	public Object getValueAt(int r, int c) {
		return model.getValueAt(rows[r].index, c);
	}

	public boolean isCellEditable(int r, int c) {
		return model.isCellEditable(rows[r].index, c);
	}

	public void setValueAt(Object aValue, int r, int c) {
		model.setValueAt(aValue, rows[r].index, c);
	}

	// 下面方法的实现把该model的方法委托为原封装的model来实现
	public int getRowCount() {
		return model.getRowCount();
	}

	public int getColumnCount() {
		return model.getColumnCount();
	}

	public String getColumnName(int c) {
		return model.getColumnName(c);
	}

	public Class getColumnClass(int c) {
		return model.getColumnClass(c);
	}

	// 定义一个Row类,该类用于封装JTable中的一行、
	// 实际上它并不封装行数据,它只封装行索引
	private class Row implements Comparable<Row> {
		// 该index保存着被封装Model里每行记录的行索引
		public int index;

		public Row(int index) {
			this.index = index;
		}

		// 实现两行之间的大小比较
		public int compareTo(Row other) {
			Object a = model.getValueAt(index, sortColumn);
			Object b = model.getValueAt(other.index, sortColumn);
			if (a instanceof Comparable) {
				return ((Comparable) a).compareTo(b);
			} else {
				return a.toString().compareTo(b.toString());
			}
		}
	}
}
参考示例2:JTable编辑单元格内容

​ 表格中的内容都是字符串,通过处理也可以是其他复杂组件。使用Renderer来处理

​ JTable用TableCellRenderer接口用于定义绘制单元格方法,Swing提供了DefaultTableCellRenderer实现类,可以绘制三种类型:IconBoolean复选按钮、Object 绘制Object的toString方法产生的字符串

​ 如果直接使用二维数组或Vector创建JTable,程序会使用JTable的匿名内部类或DefaultTableModel充当该表格的model对象,这两个TableModel的getColumnClass返回值都是Object

​ 为了能够让单元格绘制器将Icon绘制成图标,把Boolean绘制成复选框,创建JTable不能使用默认的TableModel,必须扩展TableModel类。使用扩展类创建JTable对象。另外使用 扩展了DefaultTableModel这个类的对象创建JTable,可以防止错误输入(比如在数字型表格输入字符型)

TreecellEditor接口默认实现类是DefaultCellEditor。有三个构造方法,分别使用文本框、复选框和JComboBox作为单元格编辑器

import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.table.*;
import java.io.*;

public class TestTableCellEditor {
	JFrame jf = new JFrame("使用单元格编辑器");
	JTable table;
	// 定义二维数组作为表格数据
	Object[][] tableData = {
			new Object[] { "胡伟红", 29, "女", new ImageIcon("icon/3.gif"),
					new ImageIcon("icon/3.gif"), true },
			new Object[] { "骑车上树", 56, "男", new ImageIcon("icon/1.gif"),
					new ImageIcon("icon/1.gif"), false },
			new Object[] { "风中叶", 35, "男", new ImageIcon("icon/4.gif"),
					new ImageIcon("icon/4.gif"), true },
			new Object[] { "有风起浪", 18, "女", new ImageIcon("icon/2.gif"),
					new ImageIcon("icon/2.gif"), true },
			new Object[] { "云飞日月", 2, "男", new ImageIcon("icon/5.gif"),
					new ImageIcon("icon/5.gif"), false } };
	// 定义一维数据作为列标题
	String[] columnTitle = { "姓名", "年龄", "性别", "主头像", "次头像", "是否已婚" };

	public void init() {
		// 以二维数组和一维数组来创建一个ExtendedTableModel对象
		ExtendedTableModel3 model = new ExtendedTableModel3(columnTitle,
				tableData);
		// 以ExtendedTableModel来创建JTable
		table = new JTable(model);
		table.setRowSelectionAllowed(false);
		table.setRowHeight(40);
		// 为该表格指定默认的编辑器
		table.setDefaultEditor(ImageIcon.class, new ImageCellEditor());
		// 获取最后一列
		TableColumn lastColumn = table.getColumnModel().getColumn(4);
		JComboBox editCombo = new JComboBox();
		for (int i = 1; i <= 10; i++) {
			editCombo.addItem(new ImageIcon("icon/" + i + ".gif"));
		}

		lastColumn.setCellEditor(new DefaultCellEditor(editCombo));

		// 将JTable对象放在JScrollPane中,并将该JScrollPane放在窗口中显示出来
		jf.add(new JScrollPane(table));
		jf.pack();
		jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		jf.setVisible(true);
	}

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

class ExtendedTableModel3 extends DefaultTableModel {
	// 重新提供一个构造器,该构造器的实现委托给DefaultTableModel父类
	public ExtendedTableModel3(String[] columnNames, Object[][] cells) {
		super(cells, columnNames);
	}

	// 重写getColumnClass方法,根据每列的第一个值来返回其真实的数据类型
	public Class getColumnClass(int c) {
		return getValueAt(0, c).getClass();
	}
}

// 扩展自己的TableCellEditor类
class ImageCellEditor extends DefaultCellEditor {
	// 定义文件选择器
	private JFileChooser fDialog = new JFileChooser();;
	private JTextField field = new JTextField(15);
	private JButton button = new JButton("...");

	public ImageCellEditor() {
		// 因为DefaultCellEditor没有无参数的构造器
		// 所以这里显式调用父类有参数的构造器。
		super(new JTextField());
		initEditor();
	}

	private void initEditor() {
		field.setEditable(false);
		// 为按钮添加监听器,当用户单击该按钮时,
		// 系统将出现一个文件选择器让用户选择图标文件。
		button.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent e) {
				browse();
			}
		});
		// 为文件选择器安装文件过滤器
		fDialog.addChoosableFileFilter(new javax.swing.filechooser.FileFilter() {
			public boolean accept(File f) {
				if (f.isDirectory()) {
					return true;
				}
				String extension = Utils.getExtension(f);
				if (extension != null) {
					if (extension.equals(Utils.tiff)
							|| extension.equals(Utils.tif)
							|| extension.equals(Utils.gif)
							|| extension.equals(Utils.jpeg)
							|| extension.equals(Utils.jpg)
							|| extension.equals(Utils.png)) {
						return true;
					} else {
						return false;
					}
				}
				return false;
			}

			public String getDescription() {
				return "有效的图片文件";
			}
		});
		fDialog.setAcceptAllFileFilterUsed(false);
	}

	// 重写TableCellEditor接口的getTableCellEditorComponent方法
	// 该方法返回单元格编辑器,该编辑器是一个JPanel,该容器包含一个文本框和一个按钮
	public Component getTableCellEditorComponent(JTable table, Object value,
			boolean isSelected, int row, int column) {

		this.button.setPreferredSize(new Dimension(20, 20));
		JPanel panel = new JPanel();
		panel.setLayout(new BorderLayout());
		field.setText(value.toString());
		panel.add(this.field, BorderLayout.CENTER);
		panel.add(this.button, BorderLayout.EAST);
		return panel;
	}

	public Object getCellEditorValue() {
		return new ImageIcon(field.getText());
	}

	private void browse() {
		// 设置、打开文件选择器
		fDialog.setCurrentDirectory(new File("icon"));
		int result = fDialog.showOpenDialog(null);
		// 如果单击了文件选择器的“取消”按钮
		if (result == JFileChooser.CANCEL_OPTION) {
			// 取消编辑
			super.cancelCellEditing();
			return;
		}
		// 如果单击了文件选择器的“确定”按钮
		else {
			// 设置field的内容
			field.setText("icon/" + fDialog.getSelectedFile().getName());
		}
	}
}

class Utils {
	public final static String jpeg = "jpeg";
	public final static String jpg = "jpg";
	public final static String gif = "gif";
	public final static String tiff = "tiff";
	public final static String tif = "tif";
	public final static String png = "png";
	// 获取文件扩展名的方法
	public static String getExtension(File f) {
		String ext = null;
		String s = f.getName();
		int i = s.lastIndexOf('.');
		if (i > 0 && i < s.length() - 1) {
			ext = s.substring(i + 1).toLowerCase();
		}
		return ext;
	}
}
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v3.1.3