失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > java 图形化界面编程--awt编程

java 图形化界面编程--awt编程

时间:2021-11-08 09:58:06

相关推荐

java 图形化界面编程--awt编程

二. AWT 编程

2.1 AWT简介

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

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

2.2 AWT继承体系

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

Component:代表一个能以图形化方式显示出来,并可与用户交互的对象,例如 Button 代表一个按钮,TextField 代表 一个文本框等;MenuComponent:则代表图形界面的菜单组件,包括 MenuBar (菜单条)、 Menultem (菜单项)等子类。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UghTQCkH-1648621485313)(./images/AWT组件继承体系.png)]

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

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-t9TA1vcK-1648621485316)(./images/LayoutManager.png)]

2.3 Container容器

2.3.1 Container继承体系

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0p5JXBwX-1648621485317)(./images/Container继承体系.png)]

​ Winow是可以独立存在的顶级窗口,默认使用BorderLayout管理其内部组件布局;​ Panel可以容纳其他组件,但不能独立存在,它必须内嵌其他容器中使用,默认使用FlowLayout管理其内部组件布局;​ ScrollPane 是 一个带滚动条的容器,它也不能独立存在,默认使用 BorderLayout 管理其内部组件布局;

2.3.2 常见API

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

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

2.3.3 容器演示

2.3.3.1 Window

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-B1zPJyDt-1648621485318)(./images/FrameDemo.jpg)]

import java.awt.*;public class FrameDemo {public static void main(String[] args) {//1.创建窗口对象Frame frame = new Frame("这是第一个窗口容器");//设置窗口的位置和大小frame.setBounds(100,100,500,300);//设置窗口可见frame.setVisible(true);}}

2.3.3.2 Panel

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o4vm9TBZ-1648621485320)(./images/PanelDemo.jpg)])

public class PanelDemo {public static void main(String[] args) {//1.创建Frame容器对象Frame frame = new Frame("这里在测试Panel");//2.创建Panel容器对象Panel panel = new Panel();//3.往Panel容器中添加组件panel.add(new TextField("这是一个测试文本"));panel.add(new Button("这是一个测试按钮"));//4.把Panel添加到Frame中frame.add(panel);//5.设置Frame的位置和大小frame.setBounds(30,30,500,300);//6.设置Frame可见frame.setVisible(true);}}

由于IDEA默认使用utf-8进行编码,但是当前我们执行代码是是在windows系统上,而windows操作系统的默认编码是gbk,所以会乱码,如果出现了乱码,那么只需要在运行当前代码前,设置一个jvm参数 -Dfile.encoding=gbk即可。

2.3.3.3 ScrollPane

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yWRyqb3h-1648621485321)(./images/ScrollPaneDemo.jpg)]

import java.awt.*;public class ScrollPaneDemo {public static void main(String[] args) {//1.创建Frame窗口对象Frame frame = new Frame("这里测试ScrollPane");//2.创建ScrollPane对象,并且指定默认有滚动条ScrollPane scrollPane = new ScrollPane(ScrollPane.SCROLLBARS_ALWAYS);//3.往ScrollPane中添加组件scrollPane.add(new TextField("这是测试文本"));scrollPane.add(new Button("这是测试按钮"));//4.把ScrollPane添加到Frame中frame.add(scrollPane);//5.设置Frame的位置及大小frame.setBounds(30,30,500,300);//6.设置Frame可见frame.setVisible(true);}}

程序明明向 ScrollPane 容器中添加了 一个文本框和一个按钮,但只能看到 一个按钮,却看不到文本框 ,这是为什么 呢?这是因为ScrollPane 使用 BorderLayout 布局管理器的缘故,而 BorderLayout 导致了该容器中只有一个组件被显示出来 。 下一节将向详细介绍布局管理器的知识 。

2.4 LayoutManager布局管理器

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

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

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

如果要让我么的程序在不同的操作系统下,都有相同的使用体验,那么手动设置组件的位置和大小,无疑是一种灾难,因为有太多的组件,需要分别设置不同操作系统下的大小和位置。为了解决这个问题,Java提供了LayoutManager布局管理器,可以根据运行平台来自动调整组件大小,程序员不用再手动设置组件的大小和位置了,只需要为容器选择合适的布局管理器即可。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-b9Jj0BJk-1648621485322)(./images/常用布局管理器.png)]

2.4.1 FlowLayout

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

FlowLayout 中组件的排列方向(从左向右、从右向左、从中间向两边等) , 该参数应该使用FlowLayout类的静态常量 : FlowLayout. LEFT 、 FlowLayout. CENTER 、 FlowLayout. RIGHT ,默认是左对齐。

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

代码演示:

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HaGz2kFw-1648621485324)(./images/FlowLayout.jpg)]

import java.awt.*;public class FlowLayoutDemo {public static void main(String[] args) {//1.创建Frame对象Frame frame = new Frame("这里测试FlowLayout");//2.修改Frame容器的布局管理器为FlowLayoutframe.setLayout(new FlowLayout(FlowLayout.LEFT,20,20));//3.往Frame中添加100个buttonfor (int i = 0; i < 100; i++) {frame.add(new Button("按钮"+i));}//4.设置Frame为最佳大小frame.pack();//5.设置Frame可见frame.setVisible(true);}}

2.4.2 BorderLayout

BorderLayout 将容器分为 EAST 、 SOUTH 、 WEST 、 NORTH 、 CENTER五个区域,普通组件可以被放置在这 5 个区域的任意一个中 。 BorderLayout布局 管理器的布局示意图如图所示 。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XS2WDExT-1648621485325)(./images/BorderLayout.png)]

当改变使用 BorderLayout 的容器大小时, NORTH 、 SOUTH 和 CENTER区域水平调整,而 EAST 、 WEST 和 CENTER 区域垂直调整。使用BorderLayout 有如下两个注意点:

当向使用 BorderLayout 布局管理器的容器中添加组件时 , 需要指定要添加到哪个区域中 。 如果没有指定添加到哪个区域中,则默认添加到中间区域中;如果向同一个区域中添加多个组件时 , 后放入的组件会覆盖先放入的组件;

代码演示1:

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MaAj14Dr-1648621485327)(./images/BorderLayout.jpg)]

import java.awt.*;public class BorderLayoutDemo1 {public static void main(String[] args) {//1.创建Frame对象Frame frame = new Frame("这里测试BorderLayout");//2.指定Frame对象的布局管理器为BorderLayoutframe.setLayout(new BorderLayout(30,5));//3.往Frame指定东南西北中各添加一个按钮组件frame.add(new Button("东侧按钮"), BorderLayout.EAST);frame.add(new Button("西侧按钮"), BorderLayout.WEST);frame.add(new Button("南侧按钮"), BorderLayout.SOUTH);frame.add(new Button("北侧按钮"), BorderLayout.NORTH);frame.add(new Button("中间按钮"), BorderLayout.CENTER);//4.设置Frame为最佳大小frame.pack();//5.设置Frame可见frame.setVisible(true);}}

如果不往某个区域中放入组件,那么该区域不会空白出来,而是会被其他区域占用

代码演示2:

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IlUvV7qz-1648621485328)(./images/BorderLayoutDemo2.jpg)]

import java.awt.*;public class BorderLayoutDemo2 {public static void main(String[] args) {//1.创建Frame对象Frame frame = new Frame("这里测试BorderLayout");//2.指定Frame对象的布局管理器为BorderLayoutframe.setLayout(new BorderLayout(30,5));//3.往Frame指定南,北,放入一个按钮,往中间区域放入一个Panelframe.add(new Button("南侧按钮"), BorderLayout.SOUTH);frame.add(new Button("北侧按钮"), BorderLayout.NORTH);Panel panel = new Panel();panel.add(new TextField("测试文本"));panel.add(new Button("中间按钮"));frame.add(panel, BorderLayout.CENTER);//4.设置Frame为最佳大小frame.pack();//5.设置Frame可见frame.setVisible(true);}}

2.4.3 GridLayout

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

案例:

​ 使用Frame+Panel,配合FlowLayout和GridLayout完成一个计算器效果。

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fdPlZk23-1648621485330)(./images/计算器.jpg)]

代码:

import java.awt.*;public class GridLayoutDemo{public static void main(String[] args) {//1.创建Frame对象,并且标题设置为计算器Frame frame = new Frame("计算器");//2.创建一个Panel对象,并且往Panel中放置一个TextField组件Panel p1 = new Panel();p1.add(new TextField(30));//3.把上述的Panel放入到Frame的北侧区域frame.add(p1,BorderLayout.NORTH);//4.创建一个Panel对象,并且设置其布局管理器为GridLayoutPanel p2 = new Panel();p2.setLayout(new GridLayout(3,5,4,4));//5.往上述Panel中,放置15个按钮,内容依次是:0,1,2,3,4,5,6,7,8,9,+,-,*,/,.for (int i = 0; i < 10; i++) {p2.add(new Button(i+""));}p2.add(new Button("+"));p2.add(new Button("-"));p2.add(new Button("*"));p2.add(new Button("/"));p2.add(new Button("."));//6.把上述Panel添加到Frame的中间区域中国frame.add(p2);//7.设置Frame为最佳大小frame.pack();//8.设置Frame可见frame.setVisible(true);}}

2.4.4 GridBagLayout

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-v0A9GRjh-1648621485332)(./images/GridBagLayout.png)]

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

GridBagConstraints API:

GridBagLayout使用步骤:

1.创建GridBagLaout布局管理器对象,并给容器设置该布局管理器对象;2.创建GridBagConstraints对象,并设置该对象的控制属性:gridx: 用于指定组件在网格中所处的横向索引;gridy: 用于执行组件在网格中所处的纵向索引;gridwidth: 用于指定组件横向跨越多少个网格;gridheight: 用于指定组件纵向跨越多少个网格;3.调用GridBagLayout对象的setConstraints(Component c,GridBagConstraints gbc )方法,把即将要添加到容器中的组件c和GridBagConstraints对象关联起来;4. 把组件添加到容器中;

案例:

​ 使用Frame容器,设置GridBagLayout布局管理器,实现下图中的效果:

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mYvY4hNG-1648621485333)(./images/GridBagLayout.jpg)]

演示代码:

import java.awt.*;public class GridBagLayoutDemo {public static void main(String[] args) {//1.创建Frame对象Frame frame = new Frame("这里是GridBagLayout测试");//2.创建GridBagLayout对象GridBagLayout gbl = new GridBagLayout();//3.把Frame对象的布局管理器设置为GridBagLayoutframe.setLayout(gbl);//4.创建GridBagConstraints对象GridBagConstraints gbc = new GridBagConstraints();//5.创建容量为10的Button数组Button[] bs = new Button[10];//6.遍历数组,初始化每一个Buttonfor (int i = 0; i < bs.length; i++) {bs[i] = new Button("按钮"+(i+1));}//7.设置所有的GridBagConstraints对象的fill属性为GridBagConstraints.BOTH,当有空白区域时,组件自动扩大占满空白区域gbc.fill=GridBagConstraints.BOTH;//8.设置GridBagConstraints对象的weightx设置为1,表示横向扩展比例为1gbc.weightx=1;//9.往frame中添加数组中的前3个ButtonaddComponent(frame,bs[0],gbl,gbc);addComponent(frame,bs[1],gbl,gbc);addComponent(frame,bs[2],gbl,gbc);//10.把GridBagConstraints的gridwidth设置为GridBagConstraints.REMAINDER,则表明当前组件是横向最后一个组件gbc.gridwidth=GridBagConstraints.REMAINDER;//11.把button数组中第四个按钮添加到frame中addComponent(frame,bs[3],gbl,gbc);//12.把GridBagConstraints的weighty设置为1,表示纵向扩展比例为1gbc.weighty=1;//13.把button数组中第5个按钮添加到frame中addComponent(frame,bs[4],gbl,gbc);//14.把GridBagConstaints的gridheight和gridwidth设置为2,表示纵向和横向会占用两个网格gbc.gridheight=2;gbc.gridwidth=2;//15.把button数组中第6个按钮添加到frame中addComponent(frame,bs[5],gbl,gbc);//16.把GridBagConstaints的gridheight和gridwidth设置为1,表示纵向会占用1个网格gbc.gridwidth=1;gbc.gridheight=1;//17.把button数组中第7个按钮添加到frame中addComponent(frame,bs[6],gbl,gbc);//18.把GridBagConstraints的gridwidth设置为GridBagConstraints.REMAINDER,则表明当前组件是横向最后一个组件gbc.gridwidth=GridBagConstraints.REMAINDER;//19.把button数组中第8个按钮添加到frame中addComponent(frame,bs[7],gbl,gbc);//20.把GridBagConstaints的gridwidth设置为1,表示纵向会占用1个网格gbc.gridwidth=1;//21.把button数组中第9、10个按钮添加到frame中addComponent(frame,bs[8],gbl,gbc);addComponent(frame,bs[9],gbl,gbc);//22.设置frame为最佳大小frame.pack();//23.设置frame可见frame.setVisible(true);}public static void addComponent(Container container,Component c,GridBagLayout gridBagLayout,GridBagConstraints gridBagConstraints){gridBagLayout.setConstraints(c,gridBagConstraints);container.add(c);}}

2.4.5 CardLayout

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

案例:

​ 使用Frame和Panel以及CardLayout完成下图中的效果,点击底部的按钮,切换卡片

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-D3gOcbS6-1648621485334)(./images/CardLayout.jpg)]

演示代码:

import java.awt.*;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;public class CardLayoutDemo {public static void main(String[] args) {//1.创建Frame对象Frame frame = new Frame("这里测试CardLayout");//2.创建一个String数组,存储不同卡片的名字String[] names = {"第一张","第二张","第三张","第四张","第五张"};//3.创建一个Panel容器p1,并设置其布局管理器为CardLayout,用来存放多张卡片CardLayout cardLayout = new CardLayout();Panel p1 = new Panel();p1.setLayout(cardLayout);//4.往p1中存储5个Button按钮,名字从String数组中取for (int i = 0; i < 5; i++) {p1.add(names[i],new Button(names[i]));}//5.创建一个Panel容器p2,用来存储5个按钮,完成卡片的切换Panel p2 = new Panel();//6.创建5个按钮,并给按钮设置监听器ActionListener listener = new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {String command = e.getActionCommand();switch (command){case "上一张":cardLayout.previous(p1);break;case "下一张":cardLayout.next(p1);break;case "第一张":cardLayout.first(p1);break;case "最后一张":cardLayout.last(p1);break;case "第三张":cardLayout.show(p1,"第三张");break;}}};Button b1 = new Button("上一张");Button b2 = new Button("下一张");Button b3 = new Button("第一张");Button b4 = new Button("最后一张");Button b5 = new Button("第三张");b1.addActionListener(listener);b2.addActionListener(listener);b3.addActionListener(listener);b4.addActionListener(listener);b5.addActionListener(listener);//7.把5个按钮添加到p2中p2.add(b1);p2.add(b2);p2.add(b3);p2.add(b4);p2.add(b5);//8.把p1添加到frame的中间区域frame.add(p1);//9.把p2添加到frame的底部区域frame.add(p2,BorderLayout.SOUTH);//10设置frame最佳大小并可见frame.pack();frame.setVisible(true);}}

2.4.6 BoxLayout

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

案例1:

​ 使用Frame和BoxLayout完成下图效果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VMgzSgyY-1648621485336)(./images/BoxLayout1.jpg)]

演示代码1:

import javax.swing.*;import java.awt.*;public class BoxLayoutDemo1 {public static void main(String[] args) {//1.创建Frame对象Frame frame = new Frame("这里测试BoxLayout");//2.创建BoxLayout布局管理器,并指定容器为上面的frame对象,指定组件排列方向为纵向BoxLayout boxLayout = new BoxLayout(frame, BoxLayout.Y_AXIS);frame.setLayout(boxLayout);//3.往frame对象中添加两个按钮frame.add(new Button("按钮1"));frame.add(new Button("按钮2"));//4.设置frame最佳大小,并可见frame.pack();frame.setVisible(true);}}

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

案例2:

​ 使用Frame和Box,完成下图效果:

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YK9t6XYQ-1648621485337)(./images/boxlayoutdemo2.jpg)]

演示代码2:

import javax.swing.*;import java.awt.*;public class BoxLayoutDemo2 {public static void main(String[] args) {//1.创建Frame对象Frame frame = new Frame("这里测试BoxLayout");//2.创建一个横向的Box,并添加两个按钮Box hBox = Box.createHorizontalBox();hBox.add(new Button("水平按钮一"));hBox.add(new Button("水平按钮二"));//3.创建一个纵向的Box,并添加两个按钮Box vBox = Box.createVerticalBox();vBox.add(new Button("垂直按钮一"));vBox.add(new Button("垂直按钮二"));//4.把box容器添加到frame容器中frame.add(hBox,BorderLayout.NORTH);frame.add(vBox);//5.设置frame最佳大小并可见frame.pack();frame.setVisible(true);}}

通过之前的两个BoxLayout演示,我们会发现,被它管理的容器中的组件之间是没有间隔的,不是特别的美观,但之前学习的几种布局,组件之间都会有一些间距,那使用BoxLayout如何给组件设置间距呢?

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pQd7vxq1-1648621485338)(./images/BoxLayout3.png)]

Box类中,提供了5个方便的静态方法来生成这些间隔组件:

案例3:

使用Frame和Box,完成下图效果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7eGV2iic-1648621485354)(./images/BoxLayout4.jpg)]

演示代码3:

import javax.swing.*;import java.awt.*;public class BoxLayoutDemo3 {public static void main(String[] args) {//1.创建Frame对象Frame frame = new Frame("这里测试BoxLayout");//2.创建一个横向的Box,并添加两个按钮Box hBox = Box.createHorizontalBox();hBox.add(new Button("水平按钮一"));hBox.add(Box.createHorizontalGlue());//两个方向都可以拉伸的间隔hBox.add(new Button("水平按钮二"));hBox.add(Box.createHorizontalStrut(10));//水平间隔固定,垂直间方向可以拉伸hBox.add(new Button("水平按钮3"));//3.创建一个纵向的Box,并添加两个按钮Box vBox = Box.createVerticalBox();vBox.add(new Button("垂直按钮一"));vBox.add(Box.createVerticalGlue());//两个方向都可以拉伸的间隔vBox.add(new Button("垂直按钮二"));vBox.add(Box.createVerticalStrut(10));//垂直间隔固定,水平方向可以拉伸vBox.add(new Button("垂直按钮三"));//4.把box容器添加到frame容器中frame.add(hBox, BorderLayout.NORTH);frame.add(vBox);//5.设置frame最佳大小并可见frame.pack();frame.setVisible(true);}}

2.5 AWT中常用组件

2.5.1 基本组件

这些 AWT 组件的用法比较简单,可以查阅 API 文档来获取它们各自的构方法、成员方法等详细信息。

案例:

​ 实现下图效果:

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Pgx4RC4m-1648621485354)(./images/BasicComponent.jpg)]

演示代码:

import javax.swing.*;import java.awt.*;public class BasicComponentDemo {Frame frame = new Frame("这里测试基本组件");//定义一个按钮Button ok = new Button("确认");//定义一个复选框组CheckboxGroup cbg = new CheckboxGroup();//定义一个单选框,初始处于被选中状态,并添加到cbg组中Checkbox male = new Checkbox("男", cbg, true);//定义一个单选框,初始处于未被选中状态,并添加到cbg组中Checkbox female = new Checkbox("女", cbg, false);//定义一个复选框,初始处于未被选中状态Checkbox married = new Checkbox("是否已婚?", false);//定义一个下拉选择框Choice colorChooser = new Choice();//定义一个列表选择框List colorList = new List(6, true);//定义一个5行,20列的多行文本域TextArea ta = new TextArea(5, 20);//定义一个50列的单行文本域TextField tf = new TextField(50);public void init() {//往下拉选择框中添加内容colorChooser.add("红色");colorChooser.add("绿色");colorChooser.add("蓝色");//往列表选择框中添加内容colorList.add("红色");colorList.add("绿色");colorList.add("蓝色");//创建一个装载按钮和文本框的Panel容器Panel bottom = new Panel();bottom.add(tf);bottom.add(ok);//把bottom添加到Frame的底部frame.add(bottom,BorderLayout.SOUTH);//创建一个Panel容器,装载下拉选择框,单选框和复选框Panel checkPanel = new Panel();checkPanel.add(colorChooser);checkPanel.add(male);checkPanel.add(female);checkPanel.add(married);//创建一个垂直排列的Box容器,装载 多行文本域和checkPanelBox topLeft = Box.createVerticalBox();topLeft.add(ta);topLeft.add(checkPanel);//创建一个水平排列的Box容器,装载topLeft和列表选择框Box top = Box.createHorizontalBox();top.add(topLeft);top.add(colorList);//将top添加到frame的中间区域frame.add(top);//设置frame最佳大小并可见frame.pack();frame.setVisible(true);}public static void main(String[] args) {new BasicComponentDemo().init();}}

2.5.2 对话框Dialog

2.5.2.1 Dialog

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

对话框通常依赖于其他窗口,就是通常需要有一个父窗口;对话框有非模式(non-modal)和模式(modal)两种,当某个模式对话框被打开后,该模式对话框总是位于它的父窗口之上,在模式对话框被关闭之前,父窗口无法获得焦点。

案例1:

​ 通过Frame、Button、Dialog实现下图效果:

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oBWt9jNU-1648621485355)(./images/DialogDemo1.jpg)]

演示代码1:

import java.awt.event.ActionEvent;import java.awt.event.ActionListener;public class DialogDemo1 {public static void main(String[] args) {Frame frame = new Frame("这里测试Dialog");Dialog d1 = new Dialog(frame, "模式对话框", true);Dialog d2 = new Dialog(frame, "非模式对话框", false);Button b1 = new Button("打开模式对话框");Button b2 = new Button("打开非模式对话框");//设置对话框的大小和位置d1.setBounds(20,30,300,400);d2.setBounds(20,30,300,400);//给b1和b2绑定监听事件b1.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {d1.setVisible(true);}});b2.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {d2.setVisible(true);}});//把按钮添加到frame中frame.add(b1);frame.add(b2,BorderLayout.SOUTH);//设置frame最佳大小并可见frame.pack();frame.setVisible(true);}}

在Dialog对话框中,可以根据需求,自定义内容

案例:

​ 点击按钮,弹出一个模式对话框,其内容如下:

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MkNE7zCZ-1648621485356)(./images/DialogDemo2.jpg)]

演示代码:

public class DialogDemo2 {public static void main(String[] args) {Frame frame = new Frame("这里测试Dialog");Dialog d1 = new Dialog(frame, "模式对话框", true);//往对话框中添加内容Box vBox = Box.createVerticalBox();vBox.add(new TextField(15));vBox.add(new JButton("确认"));d1.add(vBox);Button b1 = new Button("打开模式对话框");//设置对话框的大小和位置d1.setBounds(20,30,200,100);//给b1绑定监听事件b1.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {d1.setVisible(true);}});//把按钮添加到frame中frame.add(b1);//设置frame最佳大小并可见frame.pack();frame.setVisible(true);}}

2.5.2.1 FileDialog

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

案例2:

​ 使用 Frame、Button和FileDialog完成下图效果:

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NXULgiJs-1648621485357)(./images/FileDialog.jpg)]

演示代码2:

import java.awt.*;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;public class FileDialogTest {public static void main(String[] args) {Frame frame = new Frame("这里测试FileDialog");FileDialog d1 = new FileDialog(frame, "选择需要加载的文件", FileDialog.LOAD);FileDialog d2 = new FileDialog(frame, "选择需要保存的文件", FileDialog.SAVE);Button b1 = new Button("打开文件");Button b2 = new Button("保存文件");//给按钮添加事件b1.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {d1.setVisible(true);//打印用户选择的文件路径和名称System.out.println("用户选择的文件路径:"+d1.getDirectory());System.out.println("用户选择的文件名称:"+d1.getFile());}});System.out.println("-------------------------------");b2.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {d2.setVisible(true);//打印用户选择的文件路径和名称System.out.println("用户选择的文件路径:"+d2.getDirectory());System.out.println("用户选择的文件名称:"+d2.getFile());}});//添加按钮到frame中frame.add(b1);frame.add(b2,BorderLayout.SOUTH);//设置frame最佳大小并可见frame.pack();frame.setVisible(true);}}

2.6 事件处理

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

2.6.1 GUI事件处理机制

定义:

​ 当在某个组件上发生某些操作的时候,会自动的触发一段代码的执行。

在GUI事件处理机制中涉及到4个重要的概念需要理解:

事件源(Event Source):操作发生的场所,通常指某个组件,例如按钮、窗口等;

事件(Event):在事件源上发生的操作可以叫做事件,GUI会把事件都封装到一个Event对象中,如果需要知道该事件的详细信息,就可以通过Event对象来获取。

事件监听器(Event Listener):当在某个事件源上发生了某个事件,事件监听器就可以对这个事件进行处理。

注册监听:把某个事件监听器(A)通过某个事件(B)绑定到某个事件源©上,当在事件源C上发生了事件B之后,那么事件监听器A的代码就会自动执行。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qpUgTX3N-1648621485357)(./images/事件处理机制.png)]

使用步骤:

1.创建事件源组件对象;

2.自定义类,实现XxxListener接口,重写方法;

3.创建事件监听器对象(自定义类对象)

4.调用事件源组件对象的addXxxListener方法完成注册监听

案例:

​ 完成下图效果,点击确定按钮,在单行文本域内显示 hello world:

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HXnufVCC-1648621485358)(./images/EventDemo1.jpg)]

演示代码:

public class EventDemo1 {Frame frame = new Frame("这里测试事件处理");//事件源Button button = new Button("确定");TextField tf = new TextField(30);public void init(){//注册监听button.addActionListener(new MyActionListener());//添加组件到frame中frame.add(tf);frame.add(button,BorderLayout.SOUTH);//设置frame最佳大小并可见frame.pack();frame.setVisible(true);}//自定义事件监听器类private class MyActionListener implements ActionListener{@Overridepublic void actionPerformed(ActionEvent e) {System.out.println("用户点击了确定按钮");tf.setText("hello world");}}public static void main(String[] args) {new EventDemo1().init();}}

2.6.2 GUI中常见事件和事件监听器

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

2.6.2.1 事件

AWT把事件分为了两大类:

​ 1.低级事件:这类事件是基于某个特定动作的事件。比如进入、点击、拖放等动作的鼠标事件,再比如得到焦点和失去焦点等焦点事件。

​ 2.高级事件:这类事件并不会基于某个特定动作,而是根据功能含义定义的事件。

2.6.2 事件监听器

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

2.6.3 案例

案例一:

​ 通过ContainerListener监听Frame容器添加组件;

​ 通过TextListener监听TextFiled内容变化;

​ 通过ItemListener监听Choice条目选中状态变化;

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vzyWxcJ1-1648621485359)(./images/ListenerDemo1.jpg)]

演示代码一:

import java.awt.*;import java.awt.event.ContainerAdapter;import java.awt.event.ContainerEvent;import java.awt.event.TextEvent;import java.awt.event.TextListener;public class ListenerDemo1 {public static void main(String[] args) {Frame frame = new Frame("这里测试监听器");//创建一个单行文本域TextField tf = new TextField(30);//给文本域添加TextListener,监听内容的变化tf.addTextListener(new TextListener() {@Overridepublic void textValueChanged(TextEvent e) {System.out.println("当前内容:"+tf.getText());;}});//给frame注册ContainerListener监听器,监听容器中组件的添加frame.addContainerListener(new ContainerAdapter() {@Overridepublic void componentAdded(ContainerEvent e) {Component child = e.getChild();System.out.println("容器中添加了新组件:"+child);}});//添加tf到frameframe.add(tf);//设置frame最佳大小并可见frame.pack();frame.setVisible(true);}}

案例2:

​ 给Frame设置WindowListner,监听用户点击 X 的动作,如果用户点击X,则关闭当前窗口

演示代码2:

import java.awt.*;import java.awt.event.WindowAdapter;import java.awt.event.WindowEvent;public class ListenerDemo2 {public static void main(String[] args) {Frame frame = new Frame("这里测试WindowListener");frame.addWindowListener(new WindowAdapter() {@Overridepublic void windowClosing(WindowEvent e) {System.exit(0);}});frame.setBounds(200,200,500,300);frame.setVisible(true);}}

2.7 菜单组件

​ 前面讲解了如果构建GUI界面,其实就是把一些GUI的组件,按照一定的布局放入到容器中展示就可以了。在实际开发中,除了主界面,还有一类比较重要的内容就是菜单相关组件,可以通过菜单相关组件很方便的使用特定的功能,在AWT中,菜单相关组件的使用和之前学习的组件是一模一样的,只需要把菜单条、菜单、菜单项组合到一起,按照一定的布局,放入到容器中即可。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-T96SYaUJ-1648621485360)(./images/菜单1.png)]

下表中给出常见的菜单相关组件:

下图是常见菜单相关组件集成体系图:

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uGymFkB2-1648621485361)(./images/菜单项组件继承体系.png)]

菜单相关组件使用:

1.准备菜单项组件,这些组件可以是MenuItem及其子类对象

2.准备菜单组件Menu或者PopupMenu(右击弹出子菜单),把第一步中准备好的菜单项组件添加进来;

3.准备菜单条组件MenuBar,把第二步中准备好的菜单组件Menu添加进来;

4.把第三步中准备好的菜单条组件添加到窗口对象中显示。

小技巧:

1.如果要在某个菜单的菜单项之间添加分割线,那么只需要调用Menu的add(new MenuItem(-))即可。

2.如果要给某个菜单项关联快捷键功能,那么只需要在创建菜单项对象时设置即可,例如给菜单项关联 ctrl+shif+/ 快捷键,只需要:new MenuItem(“菜单项名字”,new MenuShortcut(KeyEvent.VK_Q,true);

案例1:

​ 使用awt中常用菜单组件,完成下图效果

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QMJvbNJ7-1648621485362)(./images/菜单1.png)]

演示代码1:

import java.awt.*;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;public class SimpleMenu {//创建窗口private Frame frame = new Frame("这里测试菜单相关组件");//创建菜单条组件private MenuBar menuBar = new MenuBar();//创建文件菜单组件private Menu fileMenu = new Menu("文件");//创建编辑菜单组件private Menu editMenu = new Menu("编辑");//创建新建菜单项private MenuItem newItem = new MenuItem("新建");//创建保存菜单项private MenuItem saveItem = new MenuItem("保存");//创建退出菜单项private MenuItem exitItem = new MenuItem("退出");//创建自动换行选择框菜单项private CheckboxMenuItem autoWrap = new CheckboxMenuItem("自动换行");//创建复制菜单项private MenuItem copyItem = new MenuItem("复制");//创建粘贴菜单项private MenuItem pasteItem = new MenuItem("粘贴");//创建格式菜单private Menu formatMenu = new Menu("格式");//创建注释菜单项private MenuItem commentItem = new MenuItem("注释");//创建取消注释菜单项private MenuItem cancelItem = new MenuItem("取消注释");//创建一个文本域private TextArea ta = new TextArea(6, 40);public void init(){//定义菜单事件监听器ActionListener listener = new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {String command = e.getActionCommand();ta.append("单击“"+command+"”菜单\n");if (command.equals("退出")){System.exit(0);}}};//为注释菜单项和退出菜单项注册监听器commentItem.addActionListener(listener);exitItem.addActionListener(listener);//为文件菜单fileMenu添加菜单项fileMenu.add(newItem);fileMenu.add(saveItem);fileMenu.add(exitItem);//为编辑菜单editMenu添加菜单项editMenu.add(autoWrap);editMenu.add(copyItem);editMenu.add(pasteItem);//为格式化菜单formatMenu添加菜单项formatMenu.add(commentItem);formatMenu.add(cancelItem);//将格式化菜单添加到编辑菜单中,作为二级菜单editMenu.add(new MenuItem("-"));editMenu.add(formatMenu);//将文件菜单和编辑菜单添加到菜单条中menuBar.add(fileMenu);menuBar.add(editMenu);//把菜单条设置到frame窗口上frame.setMenuBar(menuBar);//把文本域添加到frame中frame.add(ta);//设置frame最佳大小并可见frame.pack();frame.setVisible(true);}public static void main(String[] args) {new SimpleMenu().init();}}

案例2:

​ 通过PopupMenu实现下图效果:

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0L8OUJ7I-1648621485364)(./images/菜单2.png)]

实现思路:

1.创建PopubMenu菜单组件;

2.创建多个MenuItem菜单项,并添加到PopupMenu中;

3.将PopupMenu添加到目标组件中;

4.为需要右击出现PopubMenu菜单的组件,注册鼠标监听事件,当监听到用户释放右键时,弹出菜单。

演示代码2:

import java.awt.*;import java.awt.event.MouseAdapter;import java.awt.event.MouseEvent;public class PopupMenuTest {private Frame frame = new Frame("这里测试PopupMenu");//创建PopubMenu菜单private PopupMenu popupMenu = new PopupMenu();//创建菜单条private MenuItem commentItem = new MenuItem("注释");private MenuItem cancelItem = new MenuItem("取消注释");private MenuItem copyItem = new MenuItem("复制");private MenuItem pasteItem = new MenuItem("保存");//创建一个文本域private TextArea ta = new TextArea("我爱中华!!!", 6, 40);//创建一个Panelprivate Panel panel = new Panel();public void init(){//把菜单项添加到PopupMenu中popupMenu.add(commentItem);popupMenu.add(cancelItem);popupMenu.add(copyItem);popupMenu.add(pasteItem);//设置panel大小panel.setPreferredSize(new Dimension(300,100));//把PopupMenu添加到panel中panel.add(popupMenu);//为panel注册鼠标事件panel.addMouseListener(new MouseAdapter() {@Overridepublic void mouseReleased(MouseEvent e) {boolean flag = e.isPopupTrigger();//判断当前鼠标操作是不是触发PopupMenu的操作if (flag){//让PopupMenu显示在panel上,并且跟随鼠标事件发生的地方显示popupMenu.show(panel,e.getX(),e.getY());}}});//把ta添加到frame中间区域中frame.add(ta);//把panel添加到frame底部frame.add(panel,BorderLayout.SOUTH);//设置frame最佳大小,并可视;frame.pack();frame.setVisible(true);}public static void main(String[] args) {new PopupMenuTest().init();}}

2.8 绘图

​ 很多程序如各种小游戏都需要在窗口中绘制各种图形,除此之外,即使在开发JavaEE项目时, 有 时候也必须"动态"地向客户 端生成各种图形、图表,比如 图形验证码、统计图等,这都需要利用AWT的绘图功能。

2.8.1 组件绘图原理

​ 之前我们已经学习过很多组件,例如Button、Frame、Checkbox等等,不同的组件,展示出来的图形都不一样,其实这些组件展示出来的图形,其本质就是用AWT的绘图来完成的。

​ 在AWT中,真正提供绘图功能的是Graphics对象,那么Component组件和Graphics对象存在什么关系,才能让Component绘制自身图形呢?在Component类中,提供了下列三个方法来完成组件图形的绘制与刷新:

​ paint(Graphics g):绘制组件的外观;

​ update(Graphics g):内部调用paint方法,刷新组件外观;

​ repaint():调用update方法,刷新组件外观;

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XqIbexFZ-1648621485366)(./images/组件绘制图形流程.png)]

​ 一般情况下,update和paint方法是由AWT系统负责调用,如果程序要希望系统重新绘制组件,可以调用repaint方法完成。

2.8.2 Graphics类的使用

​ 实际生活中如果需要画图,首先我们得准备一张纸,然后在拿一支画笔,配和一些颜色,就可以在纸上画出来各种各样的图形,例如圆圈、矩形等等。

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6HJNefJr-1648621485367)(./images/画图示意.jpg)]

程序中绘图也一样,也需要画布,画笔,颜料等等。AWT中提供了Canvas类充当画布,提供了Graphics类来充当画笔,通过调用Graphics对象的setColor()方法可以给画笔设置颜色。

画图的步骤:

1.自定义类,继承Canvas类,重写paint(Graphics g)方法完成画图;

2.在paint方法内部,真正开始画图之前调用Graphics对象的setColor()、setFont()等方法设置画笔的颜色、字体等属性;

3.调用Graphics画笔的drawXxx()方法开始画图。

其实画图的核心就在于使用Graphics画笔在Canvas画布上画出什么颜色、什么样式的图形,所以核心在画笔上,下表中列出了Graphics类中常用的一些方法:

案例:

​ 使用AWT绘图API,完成下图效果

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qhdUSxe9-1648621485369)(./images/画图演示1.jpg)]

演示代码:

import java.awt.*;import java.awt.event.ActionEvent;import java.awt.event.ActionListener;import java.util.Random;public class SimpleDraw {private final String RECT_SHAPE="rect";private final String OVAL_SHAPE="oval";private Frame frame = new Frame("这里测试绘图");private Button drawRectBtn = new Button("绘制矩形");private Button drawOvalBtn = new Button("绘制椭圆");//用来保存当前用户需要绘制什么样的图形private String shape="";private MyCanvas drawArea = new MyCanvas();public void init(){//为按钮添加点击事件drawRectBtn.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {shape = RECT_SHAPE;drawArea.repaint();}});drawOvalBtn.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {shape = OVAL_SHAPE;drawArea.repaint();}});//定义一个Panel,装载两个按钮Panel p = new Panel();p.add(drawRectBtn);p.add(drawOvalBtn);//把panel添加到frame底部frame.add(p,BorderLayout.SOUTH);//设置画布的大小drawArea.setPreferredSize(new Dimension(300,200));//把画布添加到frame中frame.add(drawArea);frame.pack();frame.setVisible(true);}public static void main(String[] args) {new SimpleDraw().init();}//1.自定义类,继承Canvas类,重写paint方法private class MyCanvas extends Canvas{@Overridepublic void paint(Graphics g) {Random r = new Random();if (shape.equals(RECT_SHAPE)){//绘制矩形g.setColor(Color.BLACK);g.drawRect(r.nextInt(200),r.nextInt(100),40,60);}if(shape.equals(OVAL_SHAPE)){//绘制椭圆g.setColor(Color.RED);g.drawOval(r.nextInt(200),r.nextInt(100),60,40);}}}}

​ Java也可用于开发一些动画。所谓动画,就是间隔一定的时间(通常小于0 . 1秒 )重新绘制新的图像,两次绘制的图像之间差异较小,肉眼看起来就成了所谓的动画 。

​ 为了实现间隔一定的时间就重新调用组件的 repaint()方法,可以借助于 Swing 提供的Timer类,Timer类是一个定时器, 它有如下一个构造器 :

Timer(int delay, ActionListener listener): 每间隔 delay 毫秒,系统自动触发 ActionListener 监听器里的事件处理器方法,在方法内部我们就可以调用组件的repaint方法,完成组件重绘。

案例2:

​ 使用AWT画图技术及Timer定时器,完成下图中弹球小游戏。

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zBZAag4Q-1648621485370)(./images/小球运行.png)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IbONc3EP-1648621485371)(./images/小球结束.png)]

演示代码2:

import javax.swing.*;import java.awt.*;import java.awt.event.*;public class PinBall {//桌面宽度private final int TABLE_WIDTH = 300;//桌面高度private final int TABLE_HEIGHT = 400;//球拍的高度和宽度private final int RACKET_WIDTH = 60;private final int RACKET_HEIGHT = 20;//小球的大小private final int BALL_SIZE = 16;//定义小球纵向运行速度private int ySpeed = 10;//小球横向运行速度private int xSpeed = 5;//定义小球的初始坐标private int ballX = 120;private int ballY = 20;//定义球拍的初始坐标,x坐标会发生变化,y坐标不会发生变化private int rackeX = 120;private final int RACKET_Y = 340;//声明定时器private Timer timer;//定义游戏结束的标记private boolean isLose = false;//声明一个桌面private MyCanvas tableArea = new MyCanvas();//创建窗口对象private Frame frame = new Frame("弹球游戏");public void init(){//设置桌面区域的最佳大小tableArea.setPreferredSize(new Dimension(TABLE_WIDTH,TABLE_HEIGHT));//把桌面添加到frame中frame.add(tableArea);//定义键盘监听器KeyListener keyListener = new KeyAdapter(){//监听键盘 ← → 按下操作,当指定的键按下时,球拍的水平坐标分别会增加或者减少@Overridepublic void keyPressed(KeyEvent e) {int keyCode = e.getKeyCode();if (keyCode==KeyEvent.VK_LEFT){//←//没有到左边界,可以继续向左移动if (rackeX>0){rackeX-=10;}}if (keyCode==KeyEvent.VK_RIGHT){//→//没有到右边界,可以继续向右移动if (rackeX<TABLE_WIDTH-RACKET_WIDTH){rackeX+=10;}}}};//为窗口和tableArea分别添加键盘事件frame.addKeyListener(keyListener);tableArea.addKeyListener(keyListener);//定义ActionListener,用来监听小球的变化情况ActionListener timerTask = new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {//小球碰到左右边框if (ballX<=0 || ballX>=TABLE_WIDTH-BALL_SIZE){xSpeed=-xSpeed;}//小球的高度超出了球拍的位置,且横向不在球拍范围内,则游戏结束if (ballY > RACKET_Y && (ballX<rackeX || ballX>rackeX+RACKET_WIDTH)){//结束定时器timer.stop();//把游戏结束的标记设置为trueisLose = true;//重绘界面tableArea.repaint();}//如果小球横向在球拍范围内,且到达球拍位置或者到达顶端位置,则小球反弹if (ballY<=0 || (ballY>=RACKET_Y-BALL_SIZE && ballX>=rackeX && ballX<=rackeX+RACKET_WIDTH)){ySpeed=-ySpeed;}//更新小球的坐标ballX+=xSpeed;ballY+=ySpeed;//重绘桌面tableArea.repaint();}};//设置定时器,定时任务就是timerTasktimer = new Timer(100,timerTask);timer.start();//设置frame最佳大小,并可视frame.pack();frame.setVisible(true);}public static void main(String[] args) {new PinBall().init();}private class MyCanvas extends Canvas{//重写paint方法,实现绘图@Overridepublic void paint(Graphics g) {//判断游戏是否结束if (isLose){//结束g.setColor(Color.BLUE);g.setFont(new Font("Times",Font.BOLD,30));g.drawString("游戏结束!",50,200);}else{//没有结束//设置颜色并绘制小球g.setColor(Color.RED);g.fillOval(ballX,ballY,BALL_SIZE,BALL_SIZE);//设置颜色并绘制球拍g.setColor(Color.PINK);g.fillRect(rackeX,RACKET_Y,RACKET_WIDTH,RACKET_HEIGHT);}}}}

2.8.3 处理位图

​ 如果仅仅绘制一些简单的几何图形,程序的图形效果依然比较单调 。 AWT 也允许在组件上绘制位图, Graphics 提供了 drawlmage() 方法用于绘制位图,该方法需要一个Image参数一一代表位图,通过该方法就可 以绘制出指定的位图 。

位图使用步骤:

1.创建Image的子类对象BufferedImage(int width,int height,int ImageType),创建时需要指定位图的宽高及类型属性;此时相当于在内存中生成了一张图片;

2.调用BufferedImage对象的getGraphics()方法获取画笔,此时就可以往内存中的这张图片上绘图了,绘图的方法和之前学习的一模一样;

3.调用组件的drawImage()方法,一次性的内存中的图片BufferedImage绘制到特定的组件上。

使用位图绘制组件的好处:

使用位图来绘制组件,相当于实现了图的缓冲区,此时绘图时没有直接把图形绘制到组件上,而是先绘制到内存中的BufferedImage上,等全部绘制完毕,再一次性的图像显示到组件上即可,这样用户的体验会好一些。

案例:

​ 通过BufferedImage实现一个简单的手绘程序:通过鼠标可以在窗口中画图。

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0I1nmpja-1648621485371)(./images/手绘程序2.png)]

演示代码:

import java.awt.*;import java.awt.event.*;import java.awt.image.BufferedImage;public class HandDraw {//定义画图区的宽高private final int AREA_WIDTH = 500;private final int AREA_HEIGHT = 400;//定义变量,保存上一次鼠标拖动时,鼠标的坐标private int preX = -1;private int preY = -1;//定义一个右键菜单,用于设置画笔的颜色private PopupMenu colorMenu = new PopupMenu();private MenuItem redItem = new MenuItem("红色");private MenuItem greenItem = new MenuItem("绿色");private MenuItem blueItem = new MenuItem("蓝色");//定义一个BufferedImage对象private BufferedImage image = new BufferedImage(AREA_WIDTH,AREA_HEIGHT,BufferedImage.TYPE_INT_RGB);//获取BufferedImage对象关联的画笔private Graphics g = image.getGraphics();//定义窗口对象private Frame frame = new Frame("简单手绘程序");//定义画布对象private Canvas drawArea = new Canvas(){@Overridepublic void paint(Graphics g) {//把位图image绘制到0,0坐标点g.drawImage(image,0,0,null);}};//定义一个Color对象,用来保存用户设置的画笔颜色,默认为黑色private Color forceColor = Color.BLACK;public void init(){//定义颜色菜单项单击监听器ActionListener menuListener = new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {String command = e.getActionCommand();switch (command){case "红色":forceColor=Color.RED;break;case "绿色":forceColor = Color.GREEN;break;case "蓝色":forceColor = Color.BLUE;break;}}};//为三个菜单项添加点击事件redItem.addActionListener(menuListener);greenItem.addActionListener(menuListener);blueItem.addActionListener(menuListener);//把菜单项添加到右键菜单中colorMenu.add(redItem);colorMenu.add(greenItem);colorMenu.add(blueItem);//把右键菜单添加到绘图区域drawAreadrawArea.add(colorMenu);//将iamge图片背景设置为白色g.fillRect(0,0,AREA_WIDTH,AREA_HEIGHT);//设置绘图区域drawArea的大小drawArea.setPreferredSize(new Dimension(AREA_WIDTH,AREA_HEIGHT));//绘图区域drawArea设置鼠标移动监听器drawArea.addMouseMotionListener(new MouseMotionAdapter() {//用于绘制图像@Overridepublic void mouseDragged(MouseEvent e) {//按下鼠标键并拖动会触发//如果上次鼠标的坐标在绘图区域,才开始绘图if (preX>0 && preY>0){//设置当前选中的画笔颜色g.setColor(forceColor);//绘制线条,需要有两组坐标,一组是上一次鼠标拖动鼠标时的坐标,一组是现在鼠标的坐标g.drawLine(preX,preY,e.getX(),e.getY());}//更新preX和preYpreX = e.getX();preY = e.getY();//重新绘制drawArea组件drawArea.repaint();}});drawArea.addMouseListener(new MouseAdapter() {//用于弹出右键菜单@Overridepublic void mouseReleased(MouseEvent e) {//松开鼠标键会触发boolean popupTrigger = e.isPopupTrigger();if (popupTrigger){//把colorMenu显示到drawArea画图区域,并跟随鼠标显示colorMenu.show(drawArea,e.getX(),e.getY());}//当鼠标松开时,把preX和preY重置为-1preX = -1;preY = -1;}});//把drawArea添加到frame中frame.add(drawArea);//设置frame最佳大小并可见frame.pack();frame.setVisible(true);}public static void main(String[] args) {new HandDraw().init();}}

2.8.4 ImageIO的使用

在实际生活中,很多软件都支持打开本地磁盘已经存在的图片,然后进行编辑,编辑完毕后,再重新保存到本地磁盘。如果使用AWT要完成这样的功能,那么需要使用到ImageIO这个类,可以操作本地磁盘的图片文件。

案例:

​ 编写图片查看程序,支持另存操作

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5itDljh6-1648621485373)(./images/ImageIODemo.jpg)]

演示代码:

import javax.imageio.ImageIO;import java.awt.image.BufferedImage;import java.io.File;public class ReadAndSaveImage {private Frame frame = new Frame("图片查看器");private BufferedImage image;private class MyCanvas extends Canvas{@Overridepublic void paint(Graphics g) {if (image!=null){g.drawImage(image,0,0,image.getWidth(),image.getHeight(),null);}}}private MyCanvas imageComponent = new MyCanvas();public void init() throws Exception{//设置菜单项MenuBar mb = new MenuBar();Menu menu = new Menu("文件");MenuItem openItem = new MenuItem("打开");MenuItem saveItem = new MenuItem("另存为");openItem.addActionListener(e -> {//弹出对话框,选择本地图片FileDialog oDialog = new FileDialog(frame);oDialog.setVisible(true);//读取用户选择的图片String dir = oDialog.getDirectory();String file = oDialog.getFile();try {image = ImageIO.read(new File(dir,file));imageComponent.repaint();} catch (IOException e1) {e1.printStackTrace();}});saveItem.addActionListener(e -> {//弹出对话框,另存为FileDialog sDialog = new FileDialog(frame,"保存图片",FileDialog.SAVE);sDialog.setVisible(true);String dir = sDialog.getDirectory();String file = sDialog.getFile();try {ImageIO.write(image,"JPEG",new File(dir,file));} catch (IOException e1) {e1.printStackTrace();}});mb.add(menu);menu.add(openItem);menu.add(saveItem);frame.setMenuBar(mb);frame.add(imageComponent);frame.setBounds(200,200,800,600);frame.setVisible(true);frame.addWindowListener(new WindowAdapter() {@Overridepublic void windowClosing(WindowEvent e) {System.exit(0);}});}public static void main(String[] args) throws Exception {new ReadAndSaveImage().init();}}

2.8.5 五子棋

接下来,我们使用之前学习的绘图技术,做一个五子棋的游戏。

​ [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hr2oMPlm-1648621485374)(./images/五子棋.jpg)]

演示代码:

import javax.imageio.ImageIO;import javax.swing.*;import java.awt.*;import java.awt.event.*;import java.awt.image.BufferedImage;import java.io.File;public class Gobang {//定义三个BufferedImage,分别代表棋盘图、黑子图、白子图private BufferedImage table;private BufferedImage black;private BufferedImage white;//定义一个BufferedImage,代表当鼠标移动时将要下子的选择框private BufferedImage selected;//定义棋盘的宽高,这里的定义尺寸和给定的board.jpg图片的尺寸一致因为棋盘背景是通过图片加载的private final int TABLE_WIDTH = 535;private final int TABLE_HEIGHT = 536;//定义棋盘中,每行和每列可下子的数目,这个数目跟给定的board.jpg中的数目是一致的,都为15private final int BOARD_SIZE = 15;//定义每个棋子所占棋盘总宽度的大小比率;每个棋子所占宽度 535/15=35private final int RATE = TABLE_WIDTH/BOARD_SIZE;//定义棋盘有效区域与背景图坐标之间的偏移值,x坐标右移5个像素,y坐标下移6个像素private final int X_OFFSET = 5;private final int Y_OFFSET = 6;/*定义一个二维数组充当棋盘上每个位置处的棋子;该数组的索引与该棋子在棋盘上的坐标需要有一个对应关系:例如: 索引[2][3]处的棋子,对一个的真实绘制坐标应该是:xpos = 2*RATE+X_OFFSET=75;ypos = 3*RATE+Y_OFFSET=111;*/private int[][] board = new int[BOARD_SIZE][BOARD_SIZE];//如果存储0,代表没有棋子,如果存储1,代表黑棋,如果存储2,代表白棋//定义五子棋游戏窗口private JFrame f = new JFrame("五子棋游戏");//定义五子棋游戏棋盘对应的Canvas组件private class ChessBoard extends JPanel{//重写paint方法,实现绘画@Overridepublic void paint(Graphics g) {//绘制五子棋棋盘g.drawImage(table,0,0,null);//绘制选中点的红框if (selectX>0 && selectY>0){g.drawImage(selected,selectX*RATE+X_OFFSET,selectY*RATE+Y_OFFSET,null);}//遍历数组,绘制棋子for (int i = 0; i < BOARD_SIZE; i++) {for (int j = 0; j < BOARD_SIZE; j++) {//绘制黑棋if (board[i][j]==1){g.drawImage(black,i*RATE+X_OFFSET,j*RATE+Y_OFFSET,null);}//绘制白棋if (board[i][j]==2){g.drawImage(white,i*RATE+X_OFFSET,j*RATE+Y_OFFSET,null);}}}}}private ChessBoard chessBoard = new ChessBoard();//定义变量,记录当前选中的坐标点对应的boad数组中对应的棋子索引;private int selectX = -1;private int selectY = -1;//定义一个变量,记录当前用户选择下的是白棋还是黑棋还是清除,清除:0,黑棋:1,白棋:2;private int chessCategory = 1;//定义Panel,放置点击按钮Panel p = new Panel();private Button whiteBtn = new Button("白棋");private Button blackBtn = new Button("黑棋");private Button clearBtn = new Button("删除");public void updateBtnColor(Color whiteBtnColor,Color blackBtnColor,Color clearBtnColor){whiteBtn.setBackground(whiteBtnColor);blackBtn.setBackground(blackBtnColor);clearBtn.setBackground(clearBtnColor);}public void init() throws Exception{//初始化按钮的颜色updateBtnColor(Color.LIGHT_GRAY,Color.GREEN,Color.LIGHT_GRAY);whiteBtn.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {chessCategory = 2;updateBtnColor(Color.GREEN,Color.LIGHT_GRAY,Color.LIGHT_GRAY);}});blackBtn.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {chessCategory=1;updateBtnColor(Color.LIGHT_GRAY,Color.GREEN,Color.LIGHT_GRAY);}});clearBtn.addActionListener(new ActionListener() {@Overridepublic void actionPerformed(ActionEvent e) {chessCategory=0;updateBtnColor(Color.LIGHT_GRAY,Color.LIGHT_GRAY,Color.GREEN);}});p.add(whiteBtn);p.add(blackBtn);p.add(clearBtn);//把Panel放入到frame底部f.add(p,BorderLayout.SOUTH);//初始化黑棋,白棋,棋盘,选中框table = ImageIO.read(new File("awt_demo\\board.jpg"));black = ImageIO.read(new File("awt_demo\\black.gif"));white = ImageIO.read(new File("awt_demo\\white.gif"));selected = ImageIO.read(new File("awt_demo\\selected.gif"));//初始化board数组,默认情况下,所有位置处都没有棋子for (int i = 0; i < BOARD_SIZE; i++) {for (int j = 0; j < BOARD_SIZE; j++) {board[i][j]=0;}}//设置chessBoard的最佳大小chessBoard.setPreferredSize(new Dimension(TABLE_WIDTH,TABLE_HEIGHT));//给chessBoard注册鼠标监听器chessBoard.addMouseListener(new MouseAdapter() {//鼠标单击会触发@Overridepublic void mouseClicked(MouseEvent e) {//将用户鼠标的坐标,转换成棋子的坐标int xPos = (e.getX()-X_OFFSET)/RATE;int yPos = (e.getY()-Y_OFFSET)/RATE;board[xPos][yPos] = chessCategory;//重绘chessBoardchessBoard.repaint();}//当鼠标退出棋盘区域后,复位选中坐标,重绘chessBoard,要保证红色选中框显示正确@Overridepublic void mouseExited(MouseEvent e) {selectX=-1;selectY=-1;chessBoard.repaint();}});//给chessBoard注册鼠标移动监听器chessBoard.addMouseMotionListener(new MouseMotionAdapter() {//当鼠标移动时,修正selectX和selectY,重绘chessBoard,要保证红色选中框显示正确@Overridepublic void mouseMoved(MouseEvent e) {//将鼠标的坐标,转换成棋子的索引selectX = (e.getX()-X_OFFSET)/RATE;selectY = (e.getY()-Y_OFFSET)/RATE;chessBoard.repaint();}});//把chessBoard添加到Frame中f.add(chessBoard);//设置frame最佳大小并可见f.pack();f.setVisible(true);}public static void main(String[] args) throws Exception{new Gobang().init();}}

如果觉得《java 图形化界面编程--awt编程》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。