JavaSE-GUI图形用户界面
Java使用AWT和Swing完成图形用户界面编程,其中AWT的全称是抽象窗口工具集(Abstract Window Toolkit),它是SUN最早提供的GUI库,这个GUI库提供了一些基本功能,但这个GUI库的功能比较有限,所以后来又提供了Swing库
GUI概述
图形用户界面(GUI,Graphics User Interface)
JDK1.0时,SUN提供了一套基本的GUI类库,希望在所有平台下都能运行,这套基本类库被称为AWT,为Java应用程序提供了基本的图形组件.AWT是窗口框架,它从不同平台的窗口系统中抽取共同组件,当程序运行时,将这些组件的创建和动作委托给程序所在的运行平台.简而言之,当时用AWT编写图形界面应用时,程序仅指定了界面组件的位置和行为,并未提供真正的实现,JVM调用操作系统本地的图形界面来创建和平台一致的对等体.使用AWT创建的图形界面应用和所在的运行平台有相同的界面风格
1996年,Netscape公司开发了一套工作方式完全不同的GUI库,简称IFC(Internet Foundation Classes),这套GUI库的所有图形界面组件,例如文本框/按钮等都是绘制在空白窗口上的,只有窗口本身需要借助于操作系统的窗口实现.IFC真正实现了各种平台上的界面一致性.不久,SUN和Netscape合作完善了这种方法,并创建了一套新的用户界面库:Swing
AWT/Swing/辅助功能API/2D API以及拖放API共同组成了JFC(Java Foundation Classes,Java基础类库),其中Swing组件全面替代了Java1.0中的AWT组件,但保留了Java1.1中的AWT事件模型.总体上,AWT是图形用户界面编程的基础,Swing组件替代了绝大部分的AWT组件,对AWT图形用户界面编程有极好的补充和加强
Swing并没有完全替代AWT,而是建立在AWT基础之上.Swing仅提供了能力更强大的用户界面组件,即使是完全采用Swing编写的GUI程序,也依然需要使用AWT的事件处理机制.AWT组件在Swing里也有对应的实现,二者用法基本相似
AWT
所有和AWT编程相关的类都放在java.awt包以及它的子包中,AWT编程有两个基类:Component和MenuComponent
AWT容器
容器是Component的子类,因此容器对象本身也是一个组件,具有组件的所有性质,可以调用Component类的所有方法
Component类常用方法
| 常用方法 | 返回值类型 | 说明 |
|---|---|---|
| setLocation(int x,int y) | void | 设置组件的位置 |
| setSize(int width,int height) | void | 设置组件的大小 |
| setBounds(int x,int y,int width,int height) | void | 同时设置组件的位置/大小 |
| setVisible(boolean b) | void | 设置该组件的可见性 |
AWT主要提供了两种主要的容器类型
- Window:可独立存在的顶级窗口
- Panel:可作为容器容纳其他组件,但不能独立存在,必须被添加到其他容器中
- Frame对象有标题,允许通过拖拉来改变窗口的位置/大小;初始化时不可见,需要用 setVisible(true)使其显式出来;默认使用BorderLayout作为其布局管理器
- Panel可作为容器来盛装其他组件,为放置组件提供空间;不能单独存在,必须放置到其他容器中;默认使用FlowLayout作为其布局管理器
- ScrollPane是一个带滚动条的容器,它也不能独立存在,必须被添加到其他容器中;当组件占用空间过大时,ScrollPane自动产生滚动条,当然也可以通过指定特定的构造器参数来指定默认具有滚动条;默认使用BorderLayout作为其布局管理器,ScrollPane通常用于盛装其他容器,所以通常不允许改变其布局管理器
Container容器类常用方法
| 常用方法 | 返回值类型 | 说明 |
|---|---|---|
| add(Component comp) | Component | 向容器中添加其他组件(该组件可以是普通组件,也可以是容器) |
| getComponentAt(int x, int y) | Component | 返回指定点的组件 |
| getComponentCount() | int | 返回该容器内组件的数量 |
| getComponents() | Component[] | 返回该容器内的所有组件 |
| setLayout(LayoutManager mgr) | void | 设置指定的布局管理器 |
注意:如果运行乱码,需要在idea的VM options中添加 -Dfile.encoding=gbk
Panel代码案例
Frame f = new Frame("测试窗口"); // 创建一个窗口
Panel p = new Panel(); // 创建一个Panel容器
p.add(new TextField(20)); // 向Panel中添加两个组件
p.add(new Button("单击我"));
f.add(p); // 将Panel容器添加到Frame窗口中
f.setBounds(30, 40, 250, 200); // 设置窗口的大小和位置
f.setVisible(true); // 将窗口显示出来(Frame默认处于隐藏状态)
ScrollPane代码案例
Frame f = new Frame("测试窗口"); // 创建一个窗口
ScrollPane sp = new ScrollPane(ScrollPane.SCROLLBARS_ALWAYS); // 创建一个ScrollPane容器,指定总是具有滚动条
sp.add(new TextField(20)); // 向ScrollPane中添加两个组件
sp.add(new Button("点击我"));
f.add(sp); // 将ScrollPane容器添加到Frame中
f.setBounds(30, 40, 250, 200); // 设置窗口的大小和位置
f.setVisible(true); // 将窗口显示出来(Frame默认处于隐藏状态)
布局管理器
为了使生成的图形用户界面具有良好的平台无关性,Java提供了布局管理器这个工具来管理组件在容器中的布局,而不适用直接设置组件位置和大小的方式
所有的AWT容器都有默认的布局管理器,如果没有为容器指定布局管理器,则该容器使用默认的布局管理器.为容器指定布局管理器通过调用容器对象的setLayout(LayoutManager Im)来完成
FlowLayout布局管理器
在FlowLayout布局管理器中,组件像水流一样向某方向流动 (排列) ,遇到障碍(边界)就折回,重头开始排列。在默认情况下,FlowLayout布局管理器从左向右排列所有组件,遇到边界就会折回下一行重新开始
Panel和Applet默认使用FlowLayout布局管理器
| 构造方法 | 方法功能 |
|---|---|
| FlowLayout() | 使用默认 的对齐方式及默认的垂直间距、水平间距创建 FlowLayout 布局管理器。 |
| FlowLayout(int align) | 使用指定的对齐方式及默认的垂直间距、水平间距创建 FlowLayout 布局管理器。 |
| FlowLayout(int align,int hgap,int vgap) | 使用指定的对齐方式及指定的垂直问距、水平间距创建FlowLayout 布局管理器。 |
上面三个构造器的hgap/vgap代表水平间距/垂直间距,为这两个参数传入整数值即可.其中align表明FlowLayout中组件的排列方向(从左向右、从右向左、从中间向两边等),该参数应该使用FlowLayout类的静态常量:FlowLayout.LEFT、FlowLayout.CENTER、FlowLayout.RIGHT,默认是左对齐
//1.创建Frame对象
Frame frame = new Frame("这里测试FlowLayout");
//2.修改Frame容器的布局管理器为FlowLayout
frame.setLayout(new FlowLayout(FlowLayout.LEFT, 20, 20));
//3.往Frame中添加100个button
for (int i = 0; i < 100; i++) {
frame.add(new Button("按钮" + i));
}
//4.将窗口调整到最佳大小,通过Java编写用户界面程序时,很少直接设置窗口的大小,通常都是调用pack()方法来将窗口调整到最佳大小
frame.pack();
//5.设置Frame可见
frame.setVisible(true);
BorderLayout布局管理器
BorderLayout将容器分为EAST、SOUTH、WEST、NORTH、CENTER五个区域,普通组件可以被放置在这5个区域的任意一个中。BorderLayout布局管理器的布局示意图如图所示
当改变使用BorderLayout的容器大小时,NORTH、SOUTH和CENTER区域水平调整,而EAST、WEST和CENTER区域垂直调整。使用BorderLayout有如下几个注意点:
- 当向使用BorderLayout布局管理器的容器中添加组件时,需要指定要添加到哪个区域中。如果没有指定添加到哪个区域中,则默认添加到中间区域中
- 如果向同一个区域中添加多个组件时,后放入的组件会覆盖先放入的组件
- BorderLayout最多只能放置5个组件,但可以放置少于5个组件,如果某个区域没有放置组件,该区域并不会出现空白,旁边区域的组件会自动占据该区域,从而保证窗口有较好的外观
Frame/Dialog/ScrollPane默认采用BorderLayout布局管理器
当使用BorderLayout布局管理器的容器中添加组件时,应该使用BorderLayout类的几个静态常量来指定添加到哪个区域中
- BorderLayout.MORTH
- BorderLayout.SOUTH
- BorderLayout.WEST
- BorderLayout.EAST
- BorderLayout.CENTER
| 构造方法 | 方法功能 |
|---|---|
| BorderLayout() | 使用默认的水平间距、垂直间距创建BorderLayout布局管理器 |
| BorderLayout(int hgap,int vgap): | 使用指定的水平间距、垂直间距创建BorderLayout布局管理器 |
Frame f = new Frame("测试窗口");
f.setLayout(new BorderLayout(30, 5));
f.add(new Button("北"), BorderLayout.NORTH);
f.add(new Button("南"), BorderLayout.SOUTH);
f.add(new Button("东"), BorderLayout.EAST);
Panel p = new Panel();
p.add(new TextField(20));
p.add(new Button("单击我"));
f.add(p); // 默认添加到中间区域
f.pack();
f.setVisible(true);
GridLayout布局管理器
GridLayout布局管理器将容器分割成纵横线分隔的网格,每个网格所占的区域大小相同。当向使用GridLayout布局管理器的容器中添加组件时,默认从左向右、从上向下依次添加到每个网格中。与FlowLayout不同的是,放置在GridLayout布局管理器中的各组件的大小由组件所处的区域决定(每个组件将自动占满整个区域)
| 构造方法 | 方法功能 |
|---|---|
| GridLayout(int rows,in t cols) | 采用指定的行数、列数,以及默认的横向间距、纵向间距将容器分割成多个网格 |
| GridLayout(int rows,int cols,int hgap,int vgap) | 采用指定 的行数、列数 ,以及指定的横向间距、纵向间距将容器分割成多个网格 |
Frame f = new Frame("计算器");
Panel p1 = new Panel();
p1.add(new TextField(30));
f.add(p1, BorderLayout.NORTH);
Panel p2 = new Panel();
p2.setLayout(new GridLayout(3, 5, 4, 4));
String[] names = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "+", "-", "*", "/", "."};
for (int i = 0; i < names.length; i++) {
p2.add(new Button(names[i]));
}
f.add(p2);
f.pack();
f.setVisible(true);
GridBagLayout布局管理器
GridBagLayout布局管理器的功能最强大,但也最复杂,与GridLayout布局管理器不同的是,在GridBagLayout布局管理器中,一个组件可以跨越一个或多个网格,并可以设置各网格的大小互不相同,从而增加了布局的灵活性。当窗口的大小发生变化时,GridBagLayout布局管理器也可以准确地控制窗口各部分的拉伸
由于在GridBagLayout布局中,每个组件可以占用多个网格,此时,我们往容器中添加组件的时候,就需要具体的控制每个组件占用多少个网格,java提供的GridBagConstaints类,与特定的组件绑定,可以完成具体大小和跨越性的设置
GridBagConstraints API:
| 成员变量 | 含义 |
|---|---|
| gridx | 设置受该对象控制的GUI组件左上角所在网格的横向索引 |
| gridy | 设置受该对象控制的GUI组件左上角所在网格的纵向索引 |
| gridwidth | 设置受该对象控制的GUI组件横向跨越多少个网格,如果属性值为GridBagContraints.REMAIND,则表明当前组件是横向最后一个组件,如果属性值为GridBagConstraints.RELATIVE,表明当前组件是横向倒数第二个组件 |
| gridheight | 设置受该对象控制的GUI组件纵向跨越多少个网格,如果属性值为GridBagContraints.REMAIND,则表明当前组件是纵向最后一个组件,如果属性值为GridBagConstraints.RELATIVE,表明当前组件是纵向倒数第二个组件 |
| fill | 当"显示区域"大于"组件"的时候,如何调整组件 : GridBagConstraints.NONE:GUI组件不扩大 GridBagConstraints.HORIZONTAL:GUI组件水平扩大以占据空白区域 GridBagConstraints.VERTICAL:GUI组件垂直扩大以占据空白区域 GridBagConstraints.BOTH:GUI组件水平、垂直同时扩大以占据空白区域 |
| ipadx | 设置受该对象控制的GUI组件横向内部填充的大小,即在该组件最小尺寸的基础上还需要增大多少 |
| ipady | 设置受该对象控制的GUI组件纵向内部填充的大小,即在该组件最小尺寸的基础上还需要增大多少 |
| insets | 设置受该对象控制的GUI组件的外部填充的大小,即该组件边界和显示区域边界之间的距离 |
| weightx | 设置受该对象控制的GUI组件占据多余空间的水平比例,假设某个容器的水平线上包括三个GUI组件,它们的水平增加比例分别是1、2、3,但容器宽度增加60像素时,则第一个组件宽度增加10像素,第二个组件宽度增加20像素,第三个组件宽度增加30像素。如果其增加比例为0,则表示不会增加 |
| weighty | 设置受该对象控制的GUI组件占据多余空间的垂直比例 |
| anchor | 设置受该对象控制的GUI组件在其显示区域中的定位方式: GridBagConstraints.CENTER (中 间 ) GridBagConstraints.NORTH (上中 ) GridBagConstraints.NORTHWEST(左上角) GridBagConstraints.NORTHEAST(右上角) GridBagConstraints.SOUTH(下中) GridBagConstraints.SOUTHEAST(右下角) GridBagConstraints.SOUTHWEST(左下角) GridBagConstraints.EAST(右中) GridBagConstraints.WEST(左中) |
- gridx: 用于指定组件在网格中所处的横向索引
- gridy: 用于执行组件在网格中所处的纵向索引
- gridwidth: 用于指定组件横向跨越多少个网格
- gridheight: 用于指定组件纵向跨越多少个网格
- 如果希望某个组件的大小随容器的增大而增大,则必须同时设置控制该组件的GridBagConstraints对象的fill属性和weightx/weighty属性
GridBagLayout使用步骤
- 创建GridBagLaout布局管理器对象,并给容器设置该布局管理器对象
- 创建GridBagConstraints对象,并设置该对象的控制属性
- 调用GridBagLayout对象的setConstraints(Component c,GridBagConstraints gbc)方法,把即将要添加到容器中的组件c和GridBagConstraints对象关联起来
- 把组件添加到容器中
GridBagLayout代码案例
public class GridBagTest {
private Frame frame = new Frame("测试窗口");
private GridBagLayout gridBagLayout = new GridBagLayout();
private GridBagConstraints gbc = new GridBagConstraints();
private Button[] bs = new Button[10];
public void init() {
frame.setLayout(gridBagLayout);
for (int i = 0; i < bs.length; i++) {
bs[i] = new Button("按钮" + i);
}
gbc.fill = GridBagConstraints.BOTH; // 设置所有的GridBagConstraints对象的fill属性,当有空白区域时,组件自动扩大占满空白区域
gbc.weightx = 1; // 设置GridBagConstraints对象的weightx设置为1,表示横向扩展比例为1
addComponent(bs[0]); // 添加第1个按钮
addComponent(bs[1]); // 添加第2个按钮
addComponent(bs[2]); // 添加第3个按钮
gbc.gridwidth = GridBagConstraints.REMAINDER; // 表明当前组件是横向最后一个组件
addComponent(bs[3]); // 添加第4个按钮
gbc.weightx = 0; // 表示横向不会扩大
addComponent(bs[4]); // 添加第5个按钮
// 把GridBagConstraints的gridwidth和gridheight设置为2,表示横向和纵向会占用两个网格
gbc.gridwidth = 2;
gbc.gridheight = 2;
addComponent(bs[5]); // 添加第6个按钮
// 把GridBagConstraints的gridwidth和gridheight设置为1,表示横纵会占用1个网格
gbc.gridwidth = 1;
gbc.gridheight = 1;
addComponent(bs[6]); // 添加第7个按钮
gbc.gridwidth = GridBagConstraints.REMAINDER;
addComponent(bs[7]); // 添加第8个按钮
gbc.gridwidth = 1; // 占用一个网格
addComponent(bs[8]); // 添加第9个按钮
addComponent(bs[9]); // 添加第10个按钮
frame.pack();
frame.setVisible(true);
}
private void addComponent(Button button) {
gridBagLayout.setConstraints(button, gbc);
frame.add(button);
}
public static void main(String[] args) {
new GridBagTest().init();
}
}
CardLayout布局管理器
CardLayout布局管理器以时间而非空间来管理它里面的组件,它将加入容器的所有组件看成一叠卡片(每个卡片其实就是一个组件),每次只有最上面的那个Component才可见。就好像一副扑克牌,它们叠在一起,每次只有最上面的一张扑克牌才可见
| 方法名称 | 方法功能 |
|---|---|
| CardLayout() | 创建默认的CardLayout布局管理器 |
| CardLayout(int hgap,int vgap) | 通过指定卡片与容器左右边界的间距(hgap)、上下边界(vgap)的间距来创建CardLayout布局管理器 |
| first(Container target) | 显示target容器中的第一张卡片 |
| last(Container target) | 显示target容器中的最后一张卡片 |
| previous(Container target) | 显示target容器中的前一张卡片 |
| next(Container target) | 显示target容器中的后一张卡片 |
| show(Container taget,String name) | 显示target容器中指定名字的卡片 |
CardLayout代码案例
public class CardLayoutTest {
Frame frame = new Frame("这里测试CardLayout");
String[] names = {"第一张", "第二张", "第三张", "第四张", "第五张"};
public void init() {
Panel p1 = new Panel(); // 创建一个Panel容器p1,并设置其布局管理器为CardLayout,用来存放多张卡片
CardLayout cardLayout = new CardLayout();
p1.setLayout(cardLayout);
for (int i = 0; i < 5; i++) {
p1.add(names[i], new Button(names[i]));
}
Panel p2 = new Panel(); // 创建一个Panel容器p2,用来存储5个按钮,完成卡片的切换
// 创建5个按钮,并给按钮设置监听器
ActionListener listener = new ActionListener() {
@Override
public 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);
// 把5个按钮添加到p2中
p2.add(b1);
p2.add(b2);
p2.add(b3);
p2.add(b4);
p2.add(b5);
// 把p1添加到frame的中间区域
frame.add(p1);
// 把p2添加到frame的底部区域
frame.add(p2, BorderLayout.SOUTH);
// 设置frame最佳大小并可见
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
new CardLayoutTest().init();
}
}
BoxLayout布局管理器
GridBagLayout布局管理器虽然功能强大,但它实在太复杂了,所以Swing引入了一个新的布局管理器:BoxLayout.BoxLayout可以在垂直和水平两个方向上摆放GUI组件,BoxLayout提供了如下一个简单的构造器
| 方法名称 | 方法功能 |
|---|---|
| BoxLayout(Container target,int axis) | 指定创建基于target容器的BoxLayout布局管理器,该布局管理器里的组件按axis方向排列。其中axis有BoxLayout.X_AXIS(横向)和BoxLayout.Y _AXIS(纵向)两个方向 |
BoxLayout通常和Box容器(在java.swing包中)结合使用,Box是一个特殊的容器,它有点像Panel容器,但该容器默认使用BoxLayout布局管理器.大多数情况下,使用Box容器去容纳多个GUI组件,然后再把Box容器作为一个组件,添加到其他的容器中,从而形成整体窗口布局
| 方法名称 | 方法功能 |
|---|---|
| static Box createHorizontalBox() | 创建一个水平排列组件的Box容器 |
| static Box createVerticalBox() | 创建一个垂直排列组件的Box容器 |
组件之间都会有一些间距,那使用BoxLayout如何给组件设置间距呢?其实很简单,我们只需要在原有的组件需要间隔的地方,添加间隔即可,而每个间隔可以是一个组件,只不过该组件没有内容,仅仅起到一种分隔的作用——BoxLayout使用Glue(橡胶)/Strut(支架)/PigidArea(刚性区域)
- Glue:代表可以在横向/纵向两个方向上同时拉伸的空白间距
- Strut:代表可以在横向/纵向任意一个方向上拉伸的空白间距
- RigidArea代表不可拉伸的空白间距
Box类中,提供了5个方便的静态方法来生成这些间隔组件:
| 方法名称 | 方法功能 |
|---|---|
| static Component createHorizontalGlue() | 创建一条水平Glue(可在两个方向上同时拉伸的间距) |
| static Component createVerticalGlue() | 创建一条垂直Glue(可在两个方向上同时拉伸的间距) |
| static Component createHorizontalStrut(int width) | 创建一条指定宽度(宽度固定了,不能拉伸)的水平Strut(可在垂直方向上拉伸的间距) |
| static Component createVerticalStrut(int height) | 创建一条指定高度(高度固定了,不能拉伸)的垂直Strut(可在水平方向上拉伸的间距) |
BoxLayout代码案例
Frame frame = new Frame("这里测试BoxLayout");
Box hBox = Box.createHorizontalBox(); // 创建一个横向的Box,并添加两个按钮
hBox.add(new Button("水平按钮一"));
hBox.add(Box.createHorizontalGlue()); // 两个方向都可以拉伸的间隔
hBox.add(new Button("水平按钮二"));
hBox.add(Box.createHorizontalStrut(10)); // 水平方向不可拉伸,固定间隔为10;垂直间方向可以拉伸
hBox.add(new Button("水平按钮3"));
Box vBox = Box.createVerticalBox(); // 创建一个纵向的Box,并添加两个按钮
vBox.add(new Button("垂直按钮一"));
vBox.add(Box.createVerticalGlue()); // 两个方向都可以拉伸的间隔
vBox.add(new Button("垂直按钮二"));
vBox.add(Box.createVerticalStrut(10)); // 垂直间隔固定,水平方向可以拉伸
vBox.add(new Button("垂直按钮三"));
frame.add(hBox, BorderLayout.NORTH); // 把box容器添加到frame容器中
frame.add(vBox);
frame.pack();
frame.setVisible(true);
绝对定位
- 先将Container的布局管理器设为null:setLayout(null)
- 向容器中添加组件时,先调用setBounds()或setSize()方法来设置组件的大小/位置,或者直接创建GUI组件时通过构造参数指定该组件的大小/位置,然后将该组件添加到容器中
Frame frame = new Frame("测试窗口");
Button button1 = new Button("第一个按钮");
Button button2 = new Button("第二个按钮");
frame.setLayout(null);
button1.setBounds(20, 30, 90, 28);
button2.setBounds(50, 45, 120, 35);
frame.add(button1);
frame.add(button2);
frame.setVisible(true);
AWT常用组件
| 组件名 | 功能 |
|---|---|
| Button | 按钮,可接受单击操作 |
| TextArea | 多行文本域 |
| TextField | 单行文本框 |
| Checkbox | 复选框组件(也可当做单选框组件使用) |
| CheckboxGroup | 用于将多个Checkbox组件组合成一组,一组Checkbox组件将只有一个可以被选中,即全部变成单选框组件 |
| Choice | 下拉选择框 |
| List | 列表框组件,可以添加多项条目 |
| Canvas | 用于绘图的画布 |
| Label | 标签类,用于放置提示性文本 |
| Frame | 窗口,在GUI程序里通过该类创建窗口 |
| Panel | 不能单独存在基本容器类,必须放到其他容器中 |
| Scrollbar | 滑动条组件;如果需要用户输入位于某个范围的值,就可以使用滑动条组件,比如调色板中设置RGB的三个值所用的滑动条.当创建一个滑动条时,必须指定它的方向/初始值/滑块的大小/最小值和最大值 |
| ScrollPane | 带水平及垂直滚动条的容器组件 |
AWT常用组件代码案例
public class Main {
Frame frame = new Frame("这里测试基本组件");
Button ok = new Button("确认"); // 定义一个按钮
CheckboxGroup cbg = new CheckboxGroup(); // 定义一个复选框组
Checkbox male = new Checkbox("男", cbg, true); // 定义一个单选框,初始处于被选中状态,并添加到cbg组中
Checkbox female = new Checkbox("女", cbg, false); // 定义一个单选框,初始处于未被选中状态,并添加到cbg组中
Checkbox married = new Checkbox("是否已婚?", false); // 定义一个复选框,初始处于未被选中状态
Choice choice = new Choice(); // 定义一个下拉选择框
List list = new List(6, true); // 定义一个列表选择框
TextArea ta = new TextArea(5, 20); // 定义一个5行,20列的多行文本域
TextField tf = new TextField(50); // 定义一个50列的单行文本域
public void init() {
// 往下拉选择框中添加内容
choice.add("红色");
choice.add("绿色");
choice.add("蓝色");
// 往列表选择框中添加内容
list.add("红色");
list.add("绿色");
list.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(choice);
checkPanel.add(male);
checkPanel.add(female);
checkPanel.add(married);
// 创建一个垂直排列的Box容器,装载 多行文本域和checkPanel
Box topLeft = Box.createVerticalBox();
topLeft.add(ta);
topLeft.add(checkPanel);
// 创建一个水平排列的Box容器,装载topLeft和列表选择框
Box top = Box.createHorizontalBox();
top.add(topLeft);
top.add(list);
// 将top添加到frame的中间区域
frame.add(top);
// 设置frame最佳大小并可见
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
new Main().init();
}
}
对话框(Dialog)
Dialog是Window类的子类,是一个容器类,属于特殊组件。对话框是可以独立存在的顶级窗口,因此用法与普通窗口的用法几乎完全一样,但是使用对话框需要注意下面两点:
- 对话框通常依赖于其他窗口,就是通常需要有一个父窗口
- 对话框有模式(modal)和非模式(non-modal)两种;当某个模式对话框被打开后,该模式对话框总是位于它的父窗口之上
- 模式:在模式对话框被关闭之前,父窗口无法获得焦点
- 非模式:在模式对话框被关闭之前,父窗口可以获得焦点
| 方法名称 | 方法功能 |
|---|---|
| Dialog(Frame owner,String title,boolean modal) | 创建一个对话框对象: Frame:当前对话框的父窗口 String:当前对话框的标题 boolean:当前对话框是否是模式对话框,true/false |
Dialog代码案例
Frame frame = new Frame("这里测试Dialog");
Dialog dialog = new Dialog(frame, "模式对话框", true);
Box vBox = Box.createVerticalBox(); //往对话框中添加内容
vBox.add(new TextField(15));
vBox.add(new JButton("确认"));
dialog.add(vBox);
Button b1 = new Button("打开模式对话框");
dialog.setBounds(20, 30, 200, 100); //设置对话框的大小和位置
//给b1绑定监听事件
b1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
dialog.setVisible(true);
}
});
//把按钮添加到frame中
frame.add(b1);
//设置frame最佳大小并可见
frame.pack();
frame.setVisible(true);
Dialog 类还有一个子类:FileDialog,它代表一个文件对话框,用于打开或者保存文件,需要注意的是FileDialog无法指定模态或者非模态,这是因为FileDialog依赖于运行平台的实现,如果运行平台的文件对话框是模态的,那么FileDialog 也是模态的;否则就是非模态的
| 方法名称 | 方法功能 |
|---|---|
| FileDialog(Frame parent,String title,int mode) | 创建一个文件对话框: Frame:指定父窗口 String:对话框标题 int:文件对话框类型,如果指定为FileDialog.load,用于打开文件,如果指定为FileDialog.SAVE,用于保存文件 |
| String getDirectory() | 获取被打开或保存文件的绝对路径 |
| String getFile() | 获取被打开或保存文件的文件名 |
FileDialog代码案例
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() {
@Override
public void actionPerformed(ActionEvent e) {
d1.setVisible(true);
//打印用户选择的文件路径和名称
System.out.println("用户选择的文件路径:" + d1.getDirectory());
System.out.println("用户选择的文件名称:" + d1.getFile());
}
});
System.out.println("-------------------------------");
b2.addActionListener(new ActionListener() {
@Override
public 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);
事件处理机制
事件:当在某个组件上发生某些操作的时候,会自动的触发一段代码的执行
在事件处理的过程中,主要涉及三类对象
- 事件源(Event Source):操作发生的场所,通常指某个组件,例如按钮、窗口等
- 事件(Event):在事件源上发生的操作可以叫做事件,GUI会把事件都封装到一个Event对象中,如果需要知道该事件的详细信息,就可以通过Event对象来获取
- 事件监听器(Event Listener):当在某个事件源上发生了某个事件,事件监听器就可以对这个事件进行处理
注册监听:把某个事件监听器(A)通过某个事件(B)绑定到某个事件源(C)上,当在事件源C上发生了事件B之后,那么事件监听器A的代码就会自动执行
使用步骤
- 创建事件源组件对象
- 自定义类,实现XxxListener接口,重写方法
- 创建事件监听器对象(自定义类对象)
- 调用事件源组件对象的addXxxListener方法完成注册监听
事件处理代码案例
Frame frame = new Frame("测试窗口");
TextField textField = new TextField(30);
Button button = new Button("确定");
button.addActionListener((ActionEvent e) -> {
textField.setText("helloWorld");
});
frame.add(textField, BorderLayout.NORTH);
frame.add(button);
frame.pack();
frame.setVisible(true);
当外部动作在AWT组件上进行操作时,系统会自动生成事件对象,这个事件对象时EventObject子类的实例,该事件对象会触发注册到事件源上的事件监听器
AWT事件机制涉及三个成员:事件源/事件/事件监听器,其中事件源就是组件;事件是由系统自动产生的,无需程序员关心;所以,实现事件监听器就是整个事件处理的核心
事件监听器必须实现事件监听器接口,AWT提供了大量的事件监听器接口用于实现不同类型的事件监听器,用于监听不同类型的事件——AWT的事件类都是AWTEvent类的子类,AWTEvent是EventObject的子类
AWT事件分为两大类:低级事件和高级事件
- 低级事件:这类事件是基于某个特定动作的事件。比如进入、点击、拖放等动作的鼠标事件,再比如得到焦点和失去焦点等焦点事件
- 高级事件:这类事件并不会基于某个特定动作,而是根据功能含义定义的事件
低级事件
| 事件 | 触发时机 |
|---|---|
| WindowEvent | 窗口事件,当窗口状态发生改变(如打开、关闭、最大化、最小化)时触发该事件 |
| ComponentEvent | 组件事件,当组件尺寸发生变化、位置发生移动、显示/隐藏状态发生改变时触发该事件 |
| ContainerEvent | 容器事件,当容器里发生添加组件、删除组件时触发该事件 |
| FocusEvent | 焦点事件,当组件得到焦点或失去焦点时触发该事件 |
| MouseEvent | 鼠标事件,当进行单击、按下、松开、移动鼠标等动作时触发该事件 |
| KeyEvent | 键盘事件,当按键被按下、松开、单击时触发该事件 |
| PaintEvent | 组件绘制事件,该事件是一个特殊的事件类型,当GUI组件调用update/paint方法来呈现自身时触发该事件,该事件并非专用于事件处理模型 |
高级事件
| 事件 | 触发时机 |
|---|---|
| ActionEvent | 动作事件,当按钮、菜单项被单击,在TextField中按Enter键时触发 |
| AjustmentEvent | 调节事件,在滑动条上移动滑块以调节数值时触发该事件 |
| ltemEvent | 选项事件,当用户选中某项,或取消选中某项时触发该事件 |
| TextEvent | 文本事件,当文本框、文本域里的文本发生改变时触发该事件 |
不同的事件需要使用不同的监听器监听,不同的监听器需要实现不同的监听器接口,当指定事件发生后,事件监听器就会调用所包含的事件处理器(实例方法)来处理事件
事件监听器
| 事件类别 | 描述信息 | 监听器接口名 |
|---|---|---|
| WindowEvent | 窗口收到窗口级事件 | WindowListener |
| ComponentEvent | 对象移动缩放显示隐藏等 | ComponentListener |
| ContainerEvent | 容器中增加删除了组件 | ContainerListener |
| FocusEvent | 组件收到或失去焦点 | FocusListener |
| MouseEvent | 鼠标移动 | MouseMotionListener |
| MouseEvent | 鼠标点击等 | MouseListener |
| KeyEvent | 键盘输入 | KeyListener |
| ActionEvent | 激活组件 | ActionListener |
| AdjustmentEvent | 移动了滚动条等组件 | AdjustmentListener |
| ItemEvent | 选择了某些项目 | ItemListener |
| TextEvent | 文本字段或文本区发生改变 | TextListener |
点击查看代码
| 事件类别 | 监听器接口 | 适配器接口 | 触发时机 |
|---|---|---|---|
| WindowEvent | WindowListener | WindowAdapter | windowOpened:窗口首次被打开时触发 windowClosing:用户单击窗口右上角的"x"按钮时触发 windowClosed:窗口调用dispose()即将关闭时触发 windowIconified:窗口最小化时触发 windowDeiconified:窗口被恢复时触发 windowActivated:窗口被激活时触发 windowDeactivated:窗口失去激活时触发 |
| ComponentEvent | ComponentListener | ComponentAdapter | componentResized:组件大小发生改变时触发 componentMoved:组件位置发生改变时触发 componentShown:组件被显式时触发 componentHidden:组件被隐藏时触发 |
| ContainerEvent | ContainerListener | ContainerAdapter | componentAdded:向容器中添加组件时触发 componentRemoved:向容器中删除组件时触发 |
| FocusEvent | FocusListener | FocusAdapter | focusGained:组件得到焦点时触发 focusLost:组件失去焦点时触发 |
| MouseEvent | MouseListener | MouseAdapter | mouseClicked:在某个组件上单击鼠标键时触发 mousePressed:在某个组件上按下鼠标键时触发 mouseReleased:在某个组件上松开鼠标键时触发 mouseEntered:鼠标进入某个组件时触发 mouseExited:鼠标离开某个组件时触发 |
| MouseEvent | MouseMotionListener | MouseMotionAdapter | mouseDragged:在某个组件上移动鼠标,且按下鼠标键时触发 mouseMoved:在某个组件上移动鼠标,且没有按下鼠标键时触发 |
| KeyEvent | KeyListener | KeyAdapter | keyTyped:单击某个按键时触发 keyPressed:按下某个按键时触发 keyReleased:松开某个按键时触发 |
| ActionEvent | ActionListener | —— | actionPerformed:按钮/文本框/菜单项被单击时触发 |
| AdjustmentEvent | AdjustmentListener | —— | adjustmentValueChanged:滑块位置发生改变时触发 |
| ItemEvent | ItemListener | —— | itemStateChanged:某项被选中或取消选中时触发 |
| TextEvent | TextListener | —— | textValueChanged:文本组件里的文本发生改变时触发 |
事件监听器代码案例
Frame frame = new Frame("测试窗口");
Choice choice = new Choice();
choice.add("张三");
choice.add("李四");
choice.add("王五");
TextField textField = new TextField(30);
choice.addItemListener((e) -> {
Object item = e.getItem();
System.out.println("当前选择的列表项是:" + item);
});
textField.addTextListener((e) -> {
String text = textField.getText();
System.out.println("当前文本框中的内容是:" + text);
});
frame.addContainerListener(new ContainerListener() {
@Override
public void componentAdded(ContainerEvent e) {
Component child = e.getChild();
System.out.println("frame中添加了:" + child);
}
@Override
public void componentRemoved(ContainerEvent e) {
}
});
Box box = Box.createHorizontalBox();
box.add(choice);
box.add(textField);
frame.add(box);
frame.pack();
frame.setVisible(true);
关闭窗口代码案例
Frame frame = new Frame("测试窗口");
frame.setBounds(200, 200, 500, 300);
// 使用适配器模式,使得不用实现WindowListener的全部方法,直接按需写实现方法即可
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
frame.setVisible(true);
菜单组件
下表中给出常见的菜单相关组件:
| 菜单组件名称 | 功能 |
|---|---|
| MenuBar | 菜单条,菜单的容器 |
| Menu | 菜单组件,菜单项的容器.它也是Menultem的子类,所以可作为菜单项使用 |
| PopupMenu | 上下文菜单组件(右键菜单组件) |
| Menultem | 菜单项组件 |
| CheckboxMenuItem | 复选框菜单项组件 |
下图是常见菜单相关组件集成体系图:
菜单代码案例
Frame frame = new Frame("测试窗口");
MenuBar menuBar = new MenuBar();
Menu file = new Menu("文件");
Menu edit = new Menu("编辑");
Menu format = new Menu("格式");
MenuItem newItem = new MenuItem("新建");
MenuItem save = new MenuItem("保存");
MenuItem exit = new MenuItem("退出", new MenuShortcut(KeyEvent.VK_X)); // Ctrl+X
CheckboxMenuItem autoWrap = new CheckboxMenuItem("自动换行");
MenuItem copy = new MenuItem("复制");
MenuItem paste = new MenuItem("粘贴");
MenuItem comment = new MenuItem("注释 Ctrl+Shift+/", new MenuShortcut(KeyEvent.VK_SLASH, true));
MenuItem cancelComment = new MenuItem("取消注释");
TextArea textArea = new TextArea(6, 40);
exit.addActionListener((e) -> {
String command = e.getActionCommand();
textArea.append("单击" + command + "\n");
if (command.equals("退出")) {
System.exit(0);
}
});
comment.addActionListener((e) -> {
String command = e.getActionCommand();
System.out.println(command);
});
file.add(newItem);
file.add(save);
file.add(exit);
edit.add(autoWrap);
edit.addSeparator();
edit.add(copy);
edit.add(paste);
edit.add(new MenuItem("-"));
format.add(comment);
format.add(cancelComment);
edit.add(format);
menuBar.add(file);
menuBar.add(edit);
frame.setMenuBar(menuBar);
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
frame.add(textArea);
frame.pack();
frame.setVisible(true);
右键菜单代码案例
Frame frame = new Frame("测试窗口");
TextArea textArea = new TextArea(6, 40);
Panel panel = new Panel();
PopupMenu menu = new PopupMenu();
MenuItem comment = new MenuItem("注释", new MenuShortcut(KeyEvent.VK_SLASH, true));
MenuItem cancelComment = new MenuItem("取消注释");
MenuItem copy = new MenuItem("复制");
MenuItem paste = new MenuItem("粘贴");
ActionListener actionListener = e -> {
String command = e.getActionCommand();
textArea.append(command);
if (command.equals("退出")) {
System.exit(0);
}
};
menu.add(comment);
menu.add(cancelComment);
menu.add(copy);
menu.add(paste);
comment.addActionListener(actionListener);
cancelComment.addActionListener(actionListener);
copy.addActionListener(actionListener);
paste.addActionListener(actionListener);
panel.add(menu);
panel.setPreferredSize(new Dimension(400, 150));
panel.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) {
menu.show(panel, e.getX(), e.getY());
}
}
});
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
frame.add(textArea);
frame.add(panel, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
AWT绘图
很多程序如各种小游戏都需要在窗口中绘制各种图形,除此之外,即使在开发JavaEE项目时,有时候也必须"动态"地向客户端生成各种图形、图表,比如图形验证码、统计图等,这都需要利用AWT的绘图功能
之前我们已经学习过很多组件,例如Button、Frame、Checkbox等等,不同的组件,展示出来的图形都不一样,其实这些组件展示出来的图形,其本质就是用AWT的绘图来完成的
程序中绘图也一样,也需要画布,画笔,颜料等等
- Canvas类充当画布(继承自Component组件)
- Graphics类来充当画笔
- 通过调用Graphics对象的setColor()方法可以给画笔设置颜色
在Component类里提供了和绘图相关的三个方法
- paint(Graphics g):绘制组件的外观
- update(Graphics g):调用paint()方法,刷新组件外观
- repaint():调用update()方法,刷新组件外观
Graphics画笔类,可以在组件上绘制丰富多彩的几何图形和位图
Graphics画笔类常用方法
| 常用方法 | 返回值类型 | 说明 |
|---|---|---|
| setColor(Color c) | abstract void | 设置颜色 |
| setFont(Font font) | abstract void | 设置字体 |
| drawLine(int x1,int y1,int x2,int y2) | abstract void | 绘制直线 |
| drawRect(int x,int y,int width,int height) | void | 绘制矩形 |
| fillRect(int x,int y,int width,int height) | abstract void | 填充矩形区域 |
| drawRoundRect(int x,int y,int width,int height,int arcWidth,int arcHeight) | abstract void | 绘制圆角矩形 |
| fillRoundRect(int x,int y,int width,int height,int arcWidth,int arcHeight) | abstract void | 填充圆角矩形区域 |
| drawOval(int x,int y,int width,int height) | abstract void | 绘制椭圆形 |
| fillOval(int x,int y,int width,int height) | abstract void | 填充椭圆区域 |
| drawPolygon(Polygon p) | void | 绘制多边形 |
| fillPolygon(Polygon p) | void | 填充多边形区域 |
| drawPolyline(int[] xPoints,int[] yPoints,int nPoints) | abstract void | 绘制多边形 |
| fillPolygon(int[] xPoints,int[] yPoints,int nPoints) | abstract void | 填充多边形区域 |
| drawArc(int x,int y,int width,int height,int startAngle,int arcAngle) | abstract void | 绘制圆弧 |
| fillArc(int x,int y,int width,int height,int startAngle,int arcAngle) | abstract void | 填充圆弧对应的扇形区域 |
| drawPolyline(int[] xPoints,int[] yPoints,int nPoints) | abstract void | 绘制折线 |
| drawImage(Image img,int x,int y,Color bgcolor,ImageObserver observer) | abstract boolean | 绘制位图 |
代码案例:绘制矩形和椭圆
public class Main {
private final String RECT_SHAPE = "rect";
private final String OVAL_SHAPE = "oval";
private Frame frame = new Frame("测试窗口");
private Button rect = new Button("绘制矩形");
private Button oval = new Button("绘制圆形");
private MyCanvas canvas = new MyCanvas();
private String shape = "";
public void init() {
rect.addActionListener(e -> {
shape = RECT_SHAPE;
canvas.repaint();
});
oval.addActionListener(e -> {
shape = OVAL_SHAPE;
canvas.repaint();
});
Panel panel = new Panel();
panel.add(rect);
panel.add(oval);
canvas.setPreferredSize(new Dimension(250, 180));
frame.add(canvas);
frame.add(panel, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
new Main().init();
}
class MyCanvas extends Canvas {
@Override
public void paint(Graphics g) {
Random rand = new Random();
if (shape.equals(RECT_SHAPE)) {
g.setColor(new Color(220, 100, 80));
g.drawRect(rand.nextInt(200), rand.nextInt(120), 40, 60);
} else if (shape.equals(OVAL_SHAPE)) {
g.setColor(new Color(80, 100, 200));
g.fillOval(rand.nextInt(200), rand.nextInt(120), 50, 40);
}
}
}
}
代码案例:小球游戏
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;
private int rackeX = 120; // 定义球拍的初始坐标,x坐标会发生变化,y坐标不会发生变化
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.add(tableArea); // 把桌面添加到frame中
//定义键盘监听器
KeyListener keyListener = new KeyAdapter() {
// 监听键盘 ← → 按下操作,当指定的键按下时,球拍的水平坐标分别会增加或者减少
@Override
public 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() {
@Override
public 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();
// 把游戏结束的标记设置为true
isLose = 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();
}
};
// 设置定时器,定时任务就是timerTask
timer = 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方法,实现绘图
@Override
public 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);
}
}
}
}
绘制位图
AWT也允许在组件上绘制位图,Graphics提供了drawlmage()方法用于绘制位图,该方法需要一个Image参数一一代表位图,通过该方法就可 以绘制出指定的位图
位图使用步骤:
- 创建Image的子类对象BufferedImage(int width,int height,int ImageType),创建时需要指定位图的宽高及类型属性;此时相当于在内存中生成了一张图片;
- 调用BufferedImage对象的getGraphics()方法获取画笔,此时就可以往内存中的这张图片上绘图了,绘图的方法和之前学习的一模一样;
- 调用组件的drawImage()方法,一次性的内存中的图片BufferedImage绘制到特定的组件上。
使用位图绘制组件的好处:使用位图来绘制组件,相当于实现了图的缓冲区,此时绘图时没有直接把图形绘制到组件上,而是先绘制到内存中的BufferedImage上,等全部绘制完毕,再一次性的图像显示到组件上即可,这样用户的体验会好一些