Java_AWT
Java_AWT
component:组件
思路:整体窗口用于展示、通过组件进行内容添加、通过特殊组件即容器进行嵌套、通过布局管理器进行布局管理、再通过监听器赋予按钮功能
一、AWT简介
当 JDK 1.0发布时, Sun 提供了 一套基本的GUI类库,这个GUI类库希望可以在所有平台下都能运行 , 这套基本类库被称为”抽象窗口工具集 (Abstract Window Toolkit )”,它为Java应用程序提供了基本的图形组件 。 AWT是窗口框架,它从不同平台的窗口系统中抽出共同组件 , 当程序运行时,将这些组件的创建和动作委托给程序所在的运行平台 。 简而言之 ,当使用 AWT 编写图形界面应用 时, 程序仅指定了界面组件的位置和行为,并未提供真正的实现,JVM调用操作系统本地的图形界面来创建和平台 一致的对等体 。
使用AWT创建的图形界面应用和所在的运行平台有相同的界面风格 , 比如在 Windows 操作系统上,它就表现出 Windows 风格 ; 在 UNIX 操作系统上,它就表现出UNIX 风格 。 Sun 希望采用这种方式来实现 ” Write Once, Run Anywhere ” 的目标 。
二、AWT继承体系
所有和 AWT 编程相关的类都放在 java.awt 包以及它的子包中, AWT 编程中有两个组件基类 :Component和 MenuComponent。
- Component:代表一个能以图形化方式显示出来,并可与用户交互的对象,例如 Button 代表一个按钮,TextField 代表 一个文本框等;
- MenuComponent:则代表图形界面的菜单组件,包括 MenuBar (菜单条)、 Menultem (菜单项)等子类。
Object –> Component
Object –> MenuComponent
MenuComponent –> MenuItem
MenuComponent –> MenuBar
MenuItem –> menu
Component –> Choice
Component –> TextComponent
Component –> CheckBox
Component –> Lable
Component –> List
Component –> Container
Component –> Cavas
Component –> ScrollBar
Component –> Button
TextComponent –> TextArea
TextComponent –> TextField
Container –> Panel
Container –> Window
Container –> ScrollPane
其中 Container 是一种特殊的 Component,它代表一种容器,通用抽象窗口工具包(AWT)容器对象是可以包含其他AWT组件的组件。
AWT中还有一个非常重要的接口叫LayoutManager ,如果一个容器中有多个组件,那么容器就需要使用LayoutManager来管理这些组件的布局方式。
LayoutManager:布局管理器 –> GridLayout
LayoutManager:布局管理器 –> FLowLayout
LayoutManager:布局管理器 –> …
三、Container容器
object –> Component:组件
Component:组件 –> Container:特殊组件即容器
Container:特殊组件即容器 –> Window:窗口容器
Container:特殊组件即容器 –> Panel:内嵌容器
Container:特殊组件即容器 –> ScrollPane:含有滚动条的容器
Window:窗口容器 –> Frame:创建窗口
Window:窗口容器 –> Dialog:创建对话框
Panel:内嵌容器 –> Applet
Window:是可以独立存在的顶级窗口,默认使用BorderLayout管理其内部组件布局;
Panel:可以容纳其它组件,但是不能独立存在,它必须内嵌其它容器中使用,默认使用FlowLayout管理其内部组件布局;
ScrollPane是一个带滚动条的容器,它也不能独立存在,默认使用BorderLayout管理其内部组件布局。
四、常见API
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 getComponentAt(int x, int y) | 返回指定点的组件 |
int getComponentCount(); | 返回该容器内组件的数量 |
Component getComponents(); | 返回该容器内的所有组件 |
五、容器演示
1、Window
参考代码:
package com.one.container;
import java.awt.*;
public class WindowDemo {
public static void main(String[] args){
//1、创建一个窗口对象
Frame frame = new Frame("这里测试一个Window窗口");
//2、指定一个窗口位置、大小
//此处参数表示像素:即一个.表示一像素
frame.setLocation(100, 100);
frame.setSize(500, 300);
//3、设置窗口对象可见
frame.setVisible(true);
}
}
2、Panel
参考代码:
package com.one.container;
import java.awt.*;
public class PanelDemo {
public static void main(String[] args){
//1、创建一个window对象,因为panel以及其它的容器,都不能独立存在,必须依附于window存在
Frame frame = new Frame("演示Panerl");
//2、创建一个Panel对象
Panel panel = new Panel();
//3、创建一个文本框和一个按钮,并把他们放入到Panel容器中
panel.add(new TextField("这里是一个测试文本"));
panel.add(new Button("这里是一个测试按钮"));
//4、把Panel放到window中
frame.add(panel);
//5、设置window的位置以及大小
frame.setBounds(100, 100, 500, 300);
//6、设置window可见
frame.setVisible(true);
}
}
3、ScrollPane:
参考代码:
程序明明向 ScrollPane 容器中添加了 一个文本框和一个按钮,但只能看到 一个按钮,却看不到文本框 ,这是为什么 呢?这是因为ScrollPane 使用 BorderLayout 布局管理器的缘故,而 BorderLayout 导致了该容器中只有一个组件被显示出来。
package com.one.container;
import java.awt.*;
public class ScrollPaneDemo {
public static void main(String[] args) {
Frame frame = new Frame("这里演示ScrollPane");
//1、创建一个ScrollPane对象
//ScrollPane.SCROLLBARS_ALWAYS 表示总是显示进度条
ScrollPane sp = new ScrollPane(ScrollPane.SCROLLBARS_ALWAYS);
//2、往ScrollPane中添加内容
sp.add(new TextField("这是测试文本"));
sp.add(new Button("这是测试按钮"));
//3、把ScrollPane添加到frame中
frame.add(sp);
frame.setBounds(100, 100, 500, 300);
frame.setVisible(true);
}
}
六、LayoutManager布局管理器
1、概述:
之前,我们介绍了Component中有一个方法setBounds()可以设置当前容器的位置和大小,但是我们需要明确一件事情,如果我们手动的为组件设置位置大小的话,就会造成程序的不通用性, 例如:
Label label = new Label("你好,世界");
创建了一个lable组件,很多情况下,我们需要让lable组件的宽高和“你好,世界”这个字符串自身的宽高一致,这种大小称为最佳大小。由于操作系统存在差异,例如在windows上,我们要达到这样的效果,需要把该Lable组件的宽和高分别设置为100px,20px,但是在Linux操作系统上,可能需要把Lable组件的宽和高分别设置为120px,24px,才能达到同样的效果。
为了解决这个问题,Java提供了LayoutManager布局管理器,可以根据运行平台来自动调整组件大小,程序员不用再手动设置组件的大小和位置了,只需要为容器选择合适的布局管理器即可。
接口LayoutManager –> GridLayout
接口LayoutManager –> FLowLayout
接口LayoutManager –> 接口LayoutManager2
接口LayoutManager2 –> CardLayout
接口LayoutManager2 –> GridBagLayout
接口LayoutManager2 –> BorderLayout
2、FlowLayout(流式布局):
在 FlowLayout 布局管理器 中,组件像水流一样向某方向流动 (排列) ,遇到障碍(边界)就折回,重头开始排列 。在默认情况下, FlowLayout 布局管理器从左向右排列所有组件,遇到边界就会折回下一行重新开始。
构造方法 | 方法功能 |
---|---|
FlowLayout() | 使用默认 的对齐方式及默认的垂直间距、水平间距创建 FlowLayout 布局管理器。 |
FlowLayout(int align) | 使用指定的对齐方式及默认的垂直间距、水平间距创建 FlowLayout 布局管理器。 |
FlowLayout(int align,int hgap,int vgap) | 使用指定的对齐方式及指定的垂直问距、水平间距创建FlowLayout 布局管理器。 |
FlowLayout中组件的排列方向(左对齐,右对齐,居中对齐),该参数使用FlowLayout类的静态常量:FlowLayout.LEFT、FlowLayout.CENTER、FlowLayout.RIGHT,默认是左对齐。
FlowLayout中组件中间距通过整数设置,单位是像素,默认是5个像素。
参考代码:
从某一个方向开始,直到边界或者结束为止
package com.one.Layout;
import java.awt.*;
public class FlowLayoutDemo {
public static void main(String[] args) {
Frame frame = new Frame("这里测试FlowLayout");
//1、通过setLayout方法设置容器的布局管理器
// frame.setLayout(new FlowLayout(FlowLayout.LEFT, 20, 20)); //后两个参数水平间距与垂直间距(像素)
// frame.setLayout(new FlowLayout(FlowLayout.CENTER, 20, 20));
frame.setLayout(new FlowLayout(FlowLayout.RIGHT, 20, 20));
//2、添加多个按钮到frame中
for(int i = 1; i <= 100; i++){
frame.add(new Button("按钮" + i));
}
//3、设置最佳大小,pack方法
frame.pack();
frame.setVisible(true);
}
}
3、BorderLayout(边框布局):
BorderLayout 将容器分为 EAST 、 SOUTH 、 WEST 、 NORTH 、 CENTER(东、南、西、北、中)五个区域,普通组件可以被放置在这 5 个区域的任意一个中 。 BorderLayout布局 管理器的布局示意图如图所示 。
当改变使用 BorderLayout 的容器大小时, NORTH 、 SOUTH 和 CENTER区域水平调整,而 EAST 、 WEST 和 CENTER 区域垂直调整。使用BorderLayout 有如下两个注意点:
- 当向使用 BorderLayout 布局管理器的容器中添加组件时 , 需要指定要添加到哪个区域中 。 如果没有指定添加到哪个区域中,则默认添加到中间区域中;
- 如果向同一个区域中添加多个组件时 , 后放入的组件会覆盖先放入的组件;
构造方法 | 方法功能 |
---|---|
BorderLayout() | 使用默认的水平间距、垂直 间距创建 BorderLayout 布局管理器 。 |
BorderLayout(int hgap,int vgap): | 使用指定的水平间距、垂直间距创建 BorderLayout 布局管理器。 |
参考代码:
package com.one.Layout;
import java.awt.*;
public class BorderLayoutDemo1 {
public static void main(String[] args) {
Frame frame = new Frame("这里测试BorderLayout");
//1、给frame设置BorderLayout布局管理器
frame.setLayout(new BorderLayout(30, 10));
//2、往frame的指定区域添加组件
frame.add(new Button("北侧按钮"), BorderLayout.NORTH);
frame.add(new Button("南侧按钮"), BorderLayout.SOUTH);
frame.add(new Button("中测按钮"), BorderLayout.CENTER);
frame.add(new Button("东测按钮"), BorderLayout.EAST);
frame.add(new Button("西测按钮"), BorderLayout.WEST);
frame.pack(); //设置最佳大小
frame.setVisible(true);
}
}
没有放置组件的位置将会被其它组件占用,同一位置上后来的组件会替代前面的组件
package com.one.Layout;
import java.awt.*;
public class BorderLayoutDemo2 {
public static void main(String[] args) {
Frame frame = new Frame("这里测试BorderLayout");
//1、给frame设置BorderLayout布局管理器
frame.setLayout(new BorderLayout(30, 10));
//2、往frame的指定区域添加组件
frame.add(new Button("北侧按钮"), BorderLayout.NORTH);
frame.add(new Button("南侧按钮"), BorderLayout.SOUTH);
frame.add(new Button("中测按钮"), BorderLayout.CENTER);
// frame.add(new Button("东测按钮"), BorderLayout.EAST);
// frame.add(new Button("西测按钮"), BorderLayout.WEST);
frame.add(new TextField("测试文本框")); //默认放在中间,将已存在的替代了
frame.pack(); //设置最佳大小
frame.setVisible(true);
}
}
如果想要在某个区域放多个组件,就将多个组件先放到一个容器中
package com.one.Layout;
import java.awt.*;
public class BorderLayoutDemo2 {
public static void main(String[] args) {
Frame frame = new Frame("这里测试BorderLayout");
//1、给frame设置BorderLayout布局管理器
frame.setLayout(new BorderLayout(30, 10));
//2、往frame的指定区域添加组件
frame.add(new Button("北侧按钮"), BorderLayout.NORTH);
frame.add(new Button("南侧按钮"), BorderLayout.SOUTH);
// frame.add(new Button("中测按钮"), BorderLayout.CENTER);
//
//
// frame.add(new TextField("测试文本框")); //默认放在中间,将已存在的替代了
//使用Panel容器组件,放置两个组件
Panel p = new Panel();
p.add(new Button("中测按钮"));
p.add(new TextField("测试文本"));
frame.add(p);
frame.pack(); //设置最佳大小
frame.setVisible(true);
}
}
4、GridLayout(网格布局):
GridLayout 布局管理器将容器分割成纵横线分隔的网格 , 每个网格所占的区域大小相同。当向使用 GridLayout 布局管理器的容器中添加组件时, 默认从左向右、 从上向下依次添加到每个网格中 。 与 FlowLayout不同的是,放置在 GridLayout 布局管理器中的各组件的大小由组件所处的区域决定(每 个组件将自动占满整个区域) 。
构造方法 | 方法功能 |
---|---|
GridLayout(int rows,in t cols) | 采用指定的行数、列数,以及默认的横向间距、纵向间距将容器 分割成多个网格 |
GridLayout(int rows,int cols,int hgap,int vgap) | 采用指定 的行数、列 数 ,以及指定的横向间距 、 纵向间距将容器分割成多个网格。 |
参考代码:
需求:使用Frame + Panel,GridLayout完成一个简单计算机效果
package com.one.Layout;
import java.awt.*;
public class GridLayout {
public static void main(String[] args) {
//Frame默认使用BorderLayout,所以无需设置
Frame frame = new Frame("这里测试GridLayout");
//1、创建一个Panel对象,里面存放一个TextFiled组件
Panel p = new Panel();
p.add(new TextField(30)); //指定一个容纳范围,即文本框大小
//2、把当前这个Panel添加到frame的北边区域
frame.add(p, BorderLayout.NORTH);
//3、再次创建一个Panel对象,并且设置它的布局管理器为GridLayout
Panel p2 = new Panel();
p2.setLayout(new java.awt.GridLayout(3, 5, 4, 4));
//4、往Panel中添加内容
for(int i = 0; i <= 9; 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("."));
//5、把当前的Panel添加到frame的中间区域添加,即默认添加
frame.add(p2);
frame.pack();
frame.setVisible(true);
}
}
5、CridBagLayout(网格包布局):
了解即可,后续有同功能,跟简单的布局管理器。
此布局管理器的功能最强大,但是也是最复杂,与GridLayout布局管理器不同的是,在Gridlayout布局管理器中,一个组件可以跨越一个或多个网格,并可以设置网格大小互不相同,从而增加了布局的灵活性。当窗口的大小发生变化时,CridBagLayout布局管理器也可以准确地控制窗口各部分的拉伸。
由于在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 (左中) |
参考代码:
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对象的布局管理器设置为GridBagLayout
frame.setLayout(gbl);
//4.创建GridBagConstraints对象
GridBagConstraints gbc = new GridBagConstraints();
//5.创建容量为10的Button数组
Button[] bs = new Button[10];
//6.遍历数组,初始化每一个Button
for (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,表示横向扩展比例为1
gbc.weightx=1;
//9.往frame中添加数组中的前3个Button
addComponent(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,表示纵向扩展比例为1
gbc.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);
}
}
6、CardLayout(卡片布局):
CardLayout 布局管理器以时间而非空间来管理它里面的组件,它将加入容器的所有组件看成一叠卡片(每个卡片其实就是一个组件),每次只有最上面的那个 Component 才可见。就好像一副扑克牌,它们叠在一起,每次只有最上面的一张扑克牌才可见。
方法名称 | 方法功能 |
---|---|
CardLayout() | 创建默认的 CardLayout 布局管理器。 |
CardLayout(int hgap,int vgap) | 通过指定卡片与容器左右边界的间距 C hgap) 、上下边界 Cvgap) 的间距来创建 CardLayout 布局管理器. |
first(Container target) | 显示target 容器中的第一张卡片. |
last(Container target) | 显示target 容器中的最后一张卡片. |
previous(Container target) | 显示target 容器中的前一张卡片. |
next(Container target) | 显示target 容器中的后一张卡片. |
show(Container taget,String name) | 显示 target 容器中指定名字的卡片. |
参考代码:
package com.one.Layout;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/*
p1进行卡片设计
p2进行按钮设置,同时需要设置监听器
*/
public class CardLayoutDemo {
public static void main(String[] args) {
Frame frame = new Frame("这里测试CardLayout");
//1、创建一个Panel,用于存储多张卡片
Panel p1 = new Panel();
//2、创建CardLayout对象,并且把该对象设置给之前创建的容器
CardLayout cardLayout = new CardLayout();
p1.setLayout(cardLayout);
//3、往Panel中存储多个组件
String[] name = {"第一张", "第二张", "第三张", "第四张", "第五张"};
for(int i = 0; i < name.length; i++){
//当p1容器使用卡片监听器时,添加的顺序就表示其第几张
p1.add(name[i], new Button(name[i]));
}
//4、把Panel放到frame中间区域
frame.add(p1);
//5、创建另一个Panel p2 用来存储多个按钮组件
Panel p2 = new Panel();
//6、创建5个按钮组件
Button b1 = new Button("上一张");
Button b2 = new Button("下一张");
Button b3 = new Button("第一张");
Button b4 = new Button("最后一张");
Button b5 = new Button("第三张");
//7、创建一个事件监听器,监听按钮的点击动作(了解,赋予按钮对应的功能)
//使用匿名内部类的方式,给接口创建对象
ActionListener listener = new ActionListener(){
@Override
public void actionPerformed(ActionEvent e){
String actionCommand = e.getActionCommand(); //这个字符串其实就是按钮上的文字
switch (actionCommand){
case "上一张":
cardLayout.previous(p1);
break;
case "下一张":
cardLayout.next(p1);
break;
case "第一张":
cardLayout.first(p1);
break;
case "最后一张":
cardLayout.last(p1);
break;
case "第三张":
cardLayout.show(p1, "第三张"); //显示属于p1容器中的第三张
break;
default:
break;
}
}
};
//8、把当前这个时间监听器和多个按钮绑定到一起
//五个按钮对象 与 listener监听器绑定
b1.addActionListener(listener);
b2.addActionListener(listener);
b3.addActionListener(listener);
b4.addActionListener(listener);
b5.addActionListener(listener);
//9、把按钮添加到容器p2中
p2.add(b1);
p2.add(b2);
p2.add(b3);
p2.add(b4);
p2.add(b5);
//10、把p2放到frame的南边区域
frame.add(p2, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
}
}
7、BoxLayout(盒子布局):
为了简化开发,Swing引入一个新的布局管理器:BoxLayout。BoxLayout可以在垂直和水平两个方向上摆放GUI组件,BoxLayout提供了如下一个简单的构造器:
方法名称 | 方法功能 |
---|---|
BoxLayout(Container target, int axis) | 指定创建基于 target 容器的 BoxLayout 布局管理器,该布局管理器里的组件按 axis 方向排列。其中 axis 有 BoxLayout.X_AXIS( 横向)和 BoxLayout.Y _AXIS (纵向〉两个方向。 |
参考代码:
package com.one.Layout;
import java.awt.*;
import javax.swing.BoxLayout;
public class BoxLayoutDemo {
public static void main(String[] args){
Frame frame = new Frame("这里测试BoxLayout");
//1、基于frame容器,创建一个BoxLayout对象,并且,该对象存放组件时垂直存放的
//参数1:表示这个布局管理器基于哪个组件 参数2:表示这个布局管理器的排列方式
BoxLayout boxLayout = new BoxLayout(frame, BoxLayout.X_AXIS);
//2、把BoxLayout对象设置给frame
frame.setLayout(boxLayout);
//3、往frame中添加两个按钮组件
frame.add(new Button("按钮1"));
frame.add(new Button("按钮2"));
frame.pack();
frame.setVisible(true);
}
}
在java.swing包中,提供了一个新的容器Box,该容器的默认布局管理器就是BoxLayout。大多数情况下,使用Box容器去容纳多个GUI组件,然后再把Box容器作为一个组件,添加到其他的容器中,从而形成整体窗口布局。
方法名称 | 方法功能 |
---|---|
static Box createHorizontalBox() | 创建一个水平排列组件的 Box 容器 。 |
static Box createVerticalBox() | 创建一个垂直排列组件的 Box 容器 。 |
参考代码:
由于Box默认使用BoxLayout布局管理器,直接通过方法创建对象进行嵌套更为方便
package com.one.Layout;
import java.awt.*;
import javax.swing.Box;
public class BoxLayoutDemo2 {
public static void main(String[] args){
Frame frame = new Frame();
//1、创建一个水平排列组件的Box容器
Box hBox = Box.createHorizontalBox();
//2、往当前容器中添加两个按钮
hBox.add(new Button("水平按钮1"));
hBox.add(new Button("水平按钮2"));
//3、创建一个垂直排列组件的Box容器
Box vBox = Box.createVerticalBox();
//4、往当前容器中添加两个按钮
vBox.add(new Button("垂直按钮1"));
vBox.add(new Button("垂直按钮2"));
//5、把两个Box容器添加到Frame中
//frame 默认使用BorderLayout
frame.add(hBox, BorderLayout.NORTH);
frame.add(vBox);
frame.pack();
frame.setVisible(true);
}
}
Box类中,提供了5个方便的静态方法来生成这些间隔组件(作用是为了是其更为美观):
非传参的:可以随着界面一起变化,水平左右可拉伸,垂直上下可拉伸。
传参方法:根据参数,固定距离。
方法名称 | 方法功能 |
---|---|
static Component createHorizontalGlue() | 创建一条水平 Glue (可在两个方向上同时拉伸的间距) |
static Component createVerticalGlue() | 创建一条垂直 Glue (可在两个方向上同时拉伸的间距) |
static Component createHorizontalStrut(int width) | 创建一条指定宽度(宽度固定了,不能拉伸)的水平Strut (可在垂直方向上拉伸的间距) |
static Component createVerticalStrut(int height) | 创建一条指定高度(高度固定了,不能拉伸)的垂直Strut (可在水平方向上拉伸的间距) |
参考代码:
package com.one.Layout;
import java.awt.*;
import javax.swing.Box;
public class BoxLayoutDemo3 {
public static void main(String[] args){
Frame frame = new Frame();
//1、创建水平排列的Box容器
Box hBox = Box.createHorizontalBox();
//2、往Box中添加按钮,还需要在多个按钮之间添加分割
hBox.add(new Button("水平按钮一"));
hBox.add(Box.createHorizontalGlue()); //该分割在两个方向上都可以拉伸
hBox.add(new Button("水平按钮二"));
hBox.add(Box.createHorizontalStrut(30)); //给定参数的这个方法不会随着界面进行拉伸
hBox.add(new Button("水平按钮三"));
//3、创建垂直排列的Box容器
Box vBox = Box.createVerticalBox();
//4、往Box中添加按钮,还需要在多个按钮之间添加分割
vBox.add(new Button("垂直按钮一"));
vBox.add(Box.createVerticalGlue());
vBox.add(new Button("垂直按钮二"));
vBox.add(Box.createVerticalStrut(30)); //给定参数的这个方法不会随着界面进行拉伸
vBox.add(new Button("垂直按钮三"));
//5、把Box添加到frame中
frame.add(hBox, BorderLayout.NORTH);
frame.add(vBox);
frame.pack();
frame.setVisible(true);
}
}
七、AWT中常用组件:
1、基本组件:
组件名 | 功能 |
---|---|
Button | 按钮 |
Canvas | 用于绘图的画布 |
Checkbox | 复选框组件(也可当做单选框组件使用,几个复选框使用同一个复选框组) |
CheckboxGroup | 复选框组,用于将多个Checkbox 组件组合成一组, 一组 Checkbox 组件将只有一个可以被选中 , 即全部变成单选框组件 |
Choice | 下拉选择框 |
Frame | 窗口 , 在 GUI 程序里通过该类创建窗口 |
Label | 标签类,用于放置提示性文本 |
List | JU表框组件,可以添加多项条目 |
Panel | 不能单独存在基本容器类,必须放到其他容器中 |
Scrollbar | 滑动条组件。如果需要用户输入位于某个范围的值 , 就可以使用滑动条组件 ,比如调 色板中设置 RGB 的三个值所用的滑动条。当创建一个滑动条时,必须指定它的方向、初始值、 滑块的大小、最小值和最大值。 |
ScrollPane | 带水平及垂直滚动条的容器组件 |
TextArea | 多行文本域 |
TextField | 单行文本框 |
这些 AWT 组件的用法比较简单,可以查阅 API 文档来获取它们各自的构方法、成员方法等详细信息。
2、基本组件部分训练:
package com.one.component;
import java.awt.*;
import javax.swing.Box;
/*
根据运行结果,从局部到整体进行分析,每个人有所不同
*/
public class BasicComponentDemo {
//放在了成员变量处进行使用
Frame frame = new Frame("这里测试基本组件");
TextArea ta = new TextArea(5, 20); //文本域,貌似自带滑轮
Choice colorChooser = new Choice(); //下拉选择框
CheckboxGroup cbg = new CheckboxGroup(); //复选框组,用于存放多个复选框
//参数说明:第一个参数是此复选框名、第二个是存放在哪个复选框中、第三个是默认是否打开
Checkbox male = new Checkbox("男", cbg, true); //复选框组件,此处两个都是放到了cbg中形成了单选框
Checkbox female = new Checkbox("女", cbg, false);
Checkbox isMarried = new Checkbox("是否已婚?"); //复选组件
TextField tf = new TextField(50); //文本框按钮
Button ok = new Button("确认"); //确认按钮
//行数6行,默认单选,使用true使其可以多选
List colorList = new List(6, true); //列表框
//用于初始化界面
public void init(){
//组装界面
//组装选择部分
colorChooser.add("红色");
colorChooser.add("蓝色");
colorChooser.add("绿色");
Box cBox = Box.createHorizontalBox();
cBox.add(colorChooser);
cBox.add(male);
cBox.add(female);
cBox.add(isMarried);
//组装文本域和选择部分
Box topLefe = Box.createVerticalBox();
topLefe.add(ta);
topLefe.add(cBox);
colorList.add("红色");
colorList.add("蓝色");
colorList.add("绿色");
//组装顶部左右结构
Box top = Box.createHorizontalBox();
top.add(topLefe);
top.add(colorList);
//组装底部
Box bBox = Box.createHorizontalBox();
bBox.add(tf);
bBox.add(ok);
//将整体看作上下结构将其装入,frame中
Box UDBox = Box.createVerticalBox();
UDBox.add(top);
UDBox.add(bBox);
frame.add(UDBox);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args){
new BasicComponentDemo().init();
}
}
3、对话框Dialog:
Dialog 是 Window 类的子类,是一个容器类,属于特殊组件 。 对话框是可以独立存在的顶级窗口, 因此用法与普通窗口的用法几乎完全一样,但是使用对话框需要注意下面两点:
对话框通常依赖于其他窗口,就是通常需要有一个父窗口;
对话框有非模式(non-modal)和模式(modal)两种,当某个模式对话框被打开后,该模式对话框总是位于它的父窗口之上,在模式对话框被关闭之前,父窗口无法获得焦点。
方法名称 | 方法功能 |
---|---|
Dialog(Frame owner, String title, boolean modal) | 创建一个对话框对象: owner:当前对话框的父窗口 title:当前对话框的标题 modal:当前对话框是否是模式对话框,true/false |
参考代码:
package com.one.component;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/*
模式对话框打开后无法控制其父窗口
非模式对话框打开后可以控制其父窗口
*/
public class DialogDemo {
public static void main(String[] args) {
Frame frame = new Frame();
//1、创建两个对话框Dialog对象,一个模式的,一个非模式的
Dialog d1 = new Dialog(frame, "模式对话框", true);
Dialog d2 = new Dialog(frame, "非模式对话框", false);
//2、通过setBounds方法设置Dialog的位置以及大小
d1.setBounds(20, 30, 300, 200);
d2.setBounds(20, 30, 300, 200);
//3、创建两个按钮
Button b1 = new Button("打开模式对话框");
Button b2 = new Button("打开非模式对话框");
//4、需要给这两个按钮添加点击后的行为
b1.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e){
d1.setVisible(true);
}
});
b2.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e){
d2.setVisible(true);
}
});
//5、把按钮添加到frame中
frame.add(b1);
frame.add(b2, BorderLayout.SOUTH);
//设置frame最佳大小并可见
frame.pack();
frame.setVisible(true);
}
}
在Dialog对话框中,可以根据需求,自定义内容。
package com.one.component;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Box;
/*
模式对话框本身也是一个容器,可以添加其它组件
*/
public class DialogDemo2 {
public static void main(String[] args) {
Frame frame = new Frame();
//1、创建对话框Dialog对象,一个模式的,一个非模式的
Dialog d1 = new Dialog(frame, "模式对话框", true);
//创建一个垂直的Box容器,把一个文本框和一个按钮添加到Box容器中
Box vBox = Box.createVerticalBox();
vBox.add(new TextField(20));
vBox.add(new Button("确认"));
//把Box容器添加到Dialog中
d1.add(vBox);
//2、通过setBounds方法设置Dialog的位置以及大小
d1.setBounds(20, 30, 300, 200);
//3、创建一个按钮
Button b1 = new Button("打开模式对话框");
//4、需要给这一个按钮添加点击后的行为
b1.addActionListener(new ActionListener(){
@Override
public void actionPerformed(ActionEvent e){
d1.setVisible(true);
}
});
//5、把按钮添加到frame中
frame.add(b1);
//设置frame最佳大小并可见
frame.pack();
frame.setVisible(true);
}
}
4、FileDialog(文件对话框):
Dialog 类还有 一个子类 : FileDialog ,它代表一个文件对话框,用于打开或者保存 文件,需要注意的是FileDialog无法指定模态或者非模态,这是因为 FileDialog 依赖于运行平台的实现,如果运行平台的文件对话框是模态的,那么 FileDialog 也是模态的;否则就是非模态的 。
方法名称 | 方法功能 |
---|---|
FileDialog(Frame parent, String title, int mode) | 创建一个文件对话框: parent:指定父窗口 title:对话框标题 mode:文件对话框类型,如果指定为FileDialog.LOAD,用于打开文件,如果指定为FileDialog.SAVE,用于保存文件 |
String getDirectory() | 获取被打开或保存文件的绝对路径 |
String getFile() | 获取被打开或保存文件的文件名 |
参考代码:
package com.one.component;
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");
//1、创建两个FileDialog对象
FileDialog f1 = new FileDialog(frame, "选择要打开的文件", FileDialog.LOAD);
FileDialog f2 = new FileDialog(frame, "选择要保存的文件", FileDialog.SAVE);
//2、创建两个按钮
Button b1 = new Button("打开文件");
Button b2 = new Button("保存文件");
//3、给这两个按钮设置点击后的行为,获取打开或者保存文件的抽象路径名
b1.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//代码会阻塞到这里
f1.setVisible(true); //显示文件对话框
//获取选择的路径及文件
String directory = f1.getDirectory();
String file = f1.getFile();
System.out.println("打开的文件路径为:" + directory);
System.out.println("打开的文件名称为:" + file);
}
});
b2.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
f2.setVisible(true);
//获取选择的路径及文件
String directory = f2.getDirectory();
String file = f2.getFile();
System.out.println("保存的文件路径为:" + directory);
System.out.println("保存的文件名称为:" + file);
}
});
//4、把按钮添加到Frame中
frame.add(b1, BorderLayout.NORTH);
frame.add(b2, BorderLayout.SOUTH);
//设置frame最佳大小并可见
frame.pack();
frame.setVisible(true);
}
}
八、事件处理
前面介绍了如何放置各种组件,从而得到了丰富多彩的图形界面,但这些界面还不能响应用户的任何操作。比如单击前面所有窗口右上角的“X”按钮,但窗口依然不会关闭。因为在 AWT 编程中 ,所有用户的操作,都必须都需要经过一套事件处理机制来完成,而 Frame 和组件本身并没有事件处理能力 。
1、GUI事件处理机制:
1、定义:
当在某个组件上发生某些操作的时候,会自动的触发一段代码的执行。
2、在GUI事件处理机制中涉及到4个重要的概念需要理解:
事件源(Event Source):操作发生的场所,通常指某个组件,例如按钮、窗口等;
事件(Event):在事件源上发生的操作可以叫做事件,GUI会把事件都封装到一个Event对象中,如果需要知道该事件的详细信息,就可以通过Event对象来获取。
事件监听器(Event Listener):当在某个事件源上发生了某个事件,事件监听器就可以对这个事件进行处理。
注册监听:把某个事件监听器(A)通过某个事件(B)绑定到某个事件源(C)上,当在事件源C上发生了事件B之后,那么事件监听器A的代码就会自动执行。
使用步骤:
1、创建事件源组件对象;
2、自定义类,实现XxxListener接口,重写方法;
3、创建事件监听器对象(自定义类对象)
4、调用事件源组件对象的addXxxListener方法完成注册监听
参考代码:
package com.one.listener;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
/*
事件源通过addActionListener()方法,传入一个监听器(实现监听器接口的类)来注册监听器从而实现绑定
*/
public class EventDemo {
Frame frame = new Frame("这里测试事件处理");
TextField tf = new TextField(30);
//事件源
Button ok = new Button("确定");
//初始化窗口
public void init(){
//组装视图
//监听器
MyListener myListener = new MyListener();
//注册监听
ok.addActionListener(myListener); //前面我们通过匿名内部类的形式进行实现的
//把tf 和 ok 放到frame中
frame.add(tf);
frame.add(ok, BorderLayout.SOUTH);
frame.pack();
frame.setVisible(true);
}
//写一个内部类 实现ActionListener接口
private class MyListener implements ActionListener{
@Override
public void actionPerformed(ActionEvent e){
tf.setText("Hello World");
}
}
public static void main(String[] args){
new EventDemo().init();
}
}
2、GUI中常见事件和事件监听器:
事件监听器必须实现事件监听器接口, AWT 提供了大量的事件监听器接口用于实现不同类型的事件监听器,用于监听不同类型的事件 。 AWT 中提供了丰富的事件类,用于封装不同组件上所发生的特定操作, AWT 的事件类都是 AWTEvent 类的子类 , AWTEvent是 EventObject 的子类。
1、事件:
AWT把事件分为了两大类:
低级事件:这类事件是基于某个特定动作的事件。比如进入、点击、拖放等动作的鼠标事件,再比如得到焦点和失去焦点等焦点事件。
事件 | 触发时机 |
---|---|
ComponentEvent | 组件事件 , 当组件尺寸发生变化、位置发生移动、显示/隐藏状态发生改变时触发该事件。 |
ContainerEvent | 容器事件 , 当容器里发生添加组件、删除组件时触发该事件 。 |
WindowEvent | 窗口事件, 当窗口状态发生改变 ( 如打开、关闭、最大化、最小化)时触发该事件 。 |
FocusEvent | 焦点事件 , 当组件得到焦点或失去焦点时触发该事件 。 |
KeyEvent | 键盘事件 , 当按键被按下、松开、单击时触发该事件。 |
MouseEvent | 鼠标事件,当进行单击、按下、松开、移动鼠标等动作时触发该事件。 |
PaintEvent | 组件绘制事件 , 该事件是一个特殊的事件类型 , 当 GUI 组件调 用 update/paint 方法 来呈现自身时触发该事件,该事件并非专用于事件处理模型 。 |
高级事件:这类事件并不会基于某个特定动作,而是根据功能含义定义的事件。
事件 | 触发时机 |
---|---|
ActionEvent | 动作事件 ,当按钮、菜单项被单击,在 TextField 中按 Enter 键时触发 |
AjustmentEvent | 调节事件,在滑动条上移动滑块以调节数值时触发该事件。 |
ltemEvent | 选项事件,当用户选中某项, 或取消选中某项时触发该事件 。 |
TextEvent | 文本事件, 当文本框、文本域里的文本发生改变时触发该事件。 |
2、 事件监听器:
不同的事件需要使用不同的监听器监听,不同的监听器需要实现不同的监听器接口, 当指定事件发生后 , 事件监听器就会调用所包含的事件处理器(实例方法)来处理事件 。
事件类别 | 描述信息 | 监听器接口名 |
---|---|---|
ActionEvent | 激活组件 | ActionListener |
ItemEvent | 选择了某些项目 | ItemListener |
MouseEvent | 鼠标移动 | MouseMotionListener |
MouseEvent | 鼠标点击等 | MouseListener |
KeyEvent | 键盘输入 | KeyListener |
FocusEvent | 组件收到或失去焦点 | FocusListener |
AdjustmentEvent | 移动了滚动条等组件 | AdjustmentListener |
ComponentEvent | 对象移动缩放显示隐藏等 | ComponentListener |
WindowEvent | 窗口收到窗口级事件 | WindowListener |
ContainerEvent | 容器中增加删除了组件 | ContainerListener |
TextEvent | 文本字段或文本区发生改变 | TextListener |
参考代码:
通过ContainerListener监听Frame容器添加组件;
通过TextListener监听TextFiled内容变化;
通过ItemListener监听Choice条目选中状态变化;
package com.one.listener;
import javax.swing.*;
import javax.swing.text.TextAction;
import java.awt.*;
import java.awt.event.*;
public class ListenerDemo {
public static void main(String[] args){
Frame frame = new Frame("这里测试监听器");
//创建组件(事件源)
TextField tf = new TextField(30);
Choice names = new Choice(); //下拉组件
names.add("张三");
names.add("李四");
names.add("王五");
//给文本域添加TextListener,监听内部的变化
tf.addTextListener(new TextListener(){
@Override
public void textValueChanged(TextEvent e){
//目前使用英文,中文有问题
String text = tf.getText();
System.out.println("当前文本框中的内容为:" + text);
}
});
//给下拉选择框添加ItemListener,监听条目选项的变化
names.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
Object item = e.getItem();
System.out.println("当前选中的条目为:" + item);
}
});
//给frame注册ContainerListener监听器,监听容器中组件的添加
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) {
}
});
//添加到frame中
Box hBox = Box.createHorizontalBox();
hBox.add(names);
hBox.add(tf);
frame.add(hBox);
frame.pack();
frame.setVisible(true);
}
}
给Frame设置WindowListner,监听用户点击 X 的动作,如果用户点击X,则关闭当前窗口
package com.one.listener;
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");
//设置WindowListener,监听用户点击X的动作,如果点击X,则关闭窗口
//WindowAdapter用于接收窗口事件的抽象适配器类(内部重写了不使用接口的方法的空实现)
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
super.windowClosing(e);
//停止当前程序
System.exit(0);
}
});
frame.setBounds(200, 200, 500, 300);
frame.setVisible(true);
}
}
九、菜单组件
前面讲解了如果构建GUI界面,其实就是把一些GUI的组件,按照一定的布局放入到容器中展示就可以了。在实际开发中,除了主界面,还有一类比较重要的内容就是菜单相关组件,可以通过菜单相关组件很方便的使用特定的功能,在AWT中,菜单相关组件的使用和之前学习的组件是一模一样的,只需要把菜单条、菜单、菜单项组合到一起,按照一定的布局,放入到容器中即可。
下表中给出常见的菜单相关组件:
菜单项可以放到菜单中,菜单可以放到菜单条中
菜单组件名称 | 功能 |
---|---|
MenuBar | 菜单条 , 菜单的容器 。 |
Menu | 菜单组件 , 菜单项的容器 。 它也是Menultem的子类 ,所以可作为菜单项使用。 |
PopupMenu | 上下文菜单组件(右键菜单组件)。 |
Menultem | 菜单项组件 。 |
CheckboxMenuItem | 复选框菜单项组件。 |
MenuComponent –> MenuItem
MenuComponent –> MenuBar
MenuItem –> CheckBoxMenuItem
MenuItem –> Menu
Menu –> PopupMenu
Menu –> 接口MenuContainer
MenuBar –> 接口MenuContainer
菜单相关组件使用:
1.准备菜单项组件,这些组件可以是MenuItem及其子类对象;
2.准备菜单组件Menu或者PopupMenu(右击弹出子菜单),把第一步中准备好的菜单项组件添加进来;
3.准备菜单条组件MenuBar,把第二步中准备好的菜单组件Menu添加进来;
4.把第三步中准备好的菜单条组件添加到窗口对象中显示。
小技巧:
1.如果要在某个菜单的菜单项之间添加分割线,那么只需要调用Menu的add(new MenuItem(-))即可。
2.如果要给某个菜单项关联快捷键功能,那么只需要在创建菜单项对象时设置即可,例如给菜单项关联 ctrl+shif+Q快捷键,只需要:new MenuItem(“菜单项名字”,new MenuShortcut(KeyEvent.VK_Q,true);
参考代码:
菜单条->菜单->菜单项
package com.one.menu;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
//菜单条就和编译器那上面的一行一样
public class SimpleMenu {
//创建窗口
private Frame frame = new Frame("这里测试菜单相关组件");
//1、创建菜单条
MenuBar menuBar = new MenuBar();
//2、创建菜单组件
Menu fileMenu = new Menu("文件");
Menu editMenu = new Menu("编辑");
Menu formatMenu = new Menu("格式");
//3、创建菜单项组件
MenuItem auto = new MenuItem("自动换行");
MenuItem copy = new MenuItem("复制");
MenuItem paste = new MenuItem("粘贴");
//4、创建子菜单项组件
//管理快捷键,ctrl+shift+Q,true表示使用Shift
MenuItem comment = new MenuItem("注释 Ctrl+Shift+Q", new MenuShortcut(KeyEvent.VK_Q, true));
MenuItem cancelComment = new MenuItem("取消注释");
//5、创建文本域组件
TextArea ta = new TextArea(6, 40);
//初始化窗口
public void init(){
//组装视图
//给comment注册一个监听
comment.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
ta.append("你点击了菜单项:" + e.getActionCommand() + "\n");
}
});
//添加好子菜单
formatMenu.add(comment);
formatMenu.add(cancelComment);
//组装编辑菜单
editMenu.add(auto);
editMenu.add(copy);
editMenu.add(paste);
editMenu.add(formatMenu);
//组装菜单条
menuBar.add(fileMenu);
menuBar.add(editMenu);
//把菜单条放入到frame中
frame.setMenuBar(menuBar);
//放置文本域组件
frame.add(ta);
//设置frame最佳大小并可见
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args){
new SimpleMenu().init();
}
}
练习PopupMenu(右键菜单组件):
package com.one.menu;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
//关于事件,需要通过帮助文档进行辅助
public class PopupMenuTest {
private Frame frame = new Frame();
//1、创建文本域
TextArea ta = new TextArea("我爱中华\n", 6, 20);
//2、创建Panel容器
Panel p = new Panel();
//3、创建PopupMenu
PopupMenu popupMenu = new PopupMenu();
//4、创建菜单项
MenuItem comment = new MenuItem("注释");
MenuItem cancelComment = new MenuItem("取消注释");
MenuItem copy = new MenuItem("复制");
MenuItem save = new MenuItem("保存");
public void init(){
//组装视图
//实现事件监听
//创建监听器接口对象
ActionListener listener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String actionCommand = e.getActionCommand();
ta.append("你点击了:" + actionCommand + "\n");
}
};
//注册监听器
comment.addActionListener(listener);
cancelComment.addActionListener(listener);
copy.addActionListener(listener);
save.addActionListener(listener);
//添加事件项
popupMenu.add(comment);
popupMenu.add(cancelComment);
popupMenu.add(copy);
popupMenu.add(save);
p.add(popupMenu);
//设置Panel的大小
p.setPreferredSize(new Dimension(400, 300));
//给Panel注册鼠标事件,监听用户点击右键
p.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) {
super.mouseReleased(e);
boolean flag = e.isPopupTrigger(); //右键为 true
if(flag){
//显示PopupMenu
//参数:显示在哪个组件上, 显示坐标x, 显示坐标y
popupMenu.show(p, e.getX(), e.getY()); //此处使用方法直接获取了点击处的坐标
}
}
});
//放置ta与p
frame.add(ta);
frame.add(p, BorderLayout.SOUTH);
//设置frame最佳大小,并可视
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args){
new PopupMenuTest().init();
}
}
十、绘图
很多程序如各种小游戏都需要在窗口中绘制各种图形,除此之外,即使在开发JavaEE项目时, 有 时候也必须”动态”地向客户端生成各种图形、图表,比如 图形验证码、统计图等,这都需要利用AWT的绘图功能。
1、 组件绘图原理:
之前我们已经学习过很多组件,例如Button、Frame、Checkbox等等,不同的组件,展示出来的图形都不一样,其实这些组件展示出来的图形,其本质就是用AWT的绘图来完成的。
在AWT中,真正提供绘图功能的是Graphics对象,那么Component组件和Graphics对象存在什么关系,才能让Component绘制自身图形呢?在Component类中,提供了下列三个方法来完成组件图形的绘制与刷新:
paint(Graphics g):绘制组件的外观;
update(Graphics g):内部调用paint方法,刷新组件外观;
repaint():调用update方法,刷新组件外观;
一般情况下,update和paint方法是由AWT系统负责调用,如果程序要希望系统重新绘制组件,可以调用repaint方法完成。
2、Graphics对象的使用:
实际生活中如果需要画图,首先我们得准备一张纸,然后在拿一支画笔,配和一些颜色,就可以在纸上画出来各种各样的图形,例如圆圈、矩形等等。
程序中绘图也一样,也需要画布,画笔,颜料等等。AWT中提供了Canvas类充当画布,提供了Graphics类来充当画笔,通过调用Graphics对象的setColor()方法可以给画笔设置颜色。
画图的步骤:
1.自定义类,继承Canvas类,重写paint(Graphics g)方法完成画图;
2.在paint方法内部,真正开始画图之前调用Graphics对象的setColor()、setFont()等方法设置画笔的颜色、字体等属性;
3.调用Graphics画笔的drawXxx()方法开始画图。
其实画图的核心就在于使用Graphics画笔在Canvas画布上画出什么颜色、什么样式的图形,所以核心在画笔上,下表中列出了Graphics类中常用的一些方法:
方法名称 | 方法功能 |
---|---|
setColor(Color c) | 设置颜色 |
setFont(Font font) | 设置字体 |
drawLine() | 绘制直线 |
drawRect() | 绘制矩形 |
drawRoundRect() | 绘制圆角矩形 |
drawOval() | 绘制椭圆形 |
drawPolygon() | 绘制多边形 |
drawArc() | 绘制圆弧 |
drawPolyline() | 绘制折线 |
fillRect() | 填充矩形区域 |
fillRoundRect() | 填充圆角矩形区域 |
fillOval() | 填充椭圆区域 |
fillPolygon() | 填充多边形区域 |
fillArc() | 填充圆弧对应的扇形区域 |
drawImage() | 绘制位图 |
参考代码:
package com.one.draw;
import com.one.menu.SimpleMenu;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class SimpleDraw {
private final String REACT_SHAPE = "react";
private final String OVAL_SHAPE = "oval";
private Frame frame = new Frame("这里测试绘图");
Button btnReact = new Button("绘制矩形");
Button btnOval = new Button("绘制椭圆");
//定义一个变量,记录当前要绘制椭圆还是矩形
private String shape = "";
//1.自定义类,继承Canvas类,重写paint(Graphics g)方法完成画图;
private class MyCanvas extends Canvas{
@Override
public void paint(Graphics g) {
//绘制不同的图形
if(shape.equals(REACT_SHAPE)){
//绘制矩形
g.setColor(Color.BLACK); //设置当前画笔的颜色为黑色
g.drawRect(100, 100, 150, 100);
}
else if(shape.equals(OVAL_SHAPE)){
//绘制椭圆
g.setColor(Color.RED);
g.drawOval(100, 100, 150, 100);
}
}
}
//创建自定义的画布对象
MyCanvas drawArea = new MyCanvas();
//注册监听
public void init(){
//组装视图
btnReact.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//修改标记值为react
shape = REACT_SHAPE;
drawArea.repaint();
}
});
btnOval.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//修改标记值为Oval
shape = OVAL_SHAPE;
drawArea.repaint();
}
});
//创建一个Panel,承载按钮
Panel p = new Panel();
p.add(btnReact);
p.add(btnOval);
frame.add(p, BorderLayout.SOUTH);
//drawArea的大小需要设置
drawArea.setPreferredSize(new Dimension(500, 500));
frame.add(drawArea);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args){
new SimpleDraw().init();
}
}
Java也可用于开发一些动画。所谓动画,就是间隔一定的时间(通常小于0 . 1秒 )重新绘制新的图像,两次绘制的图像之间差异较小,肉眼看起来就成了所谓的动画 。
为了实现间隔一定的时间就重新调用组件的 repaint()方法,可以借助于 Swing 提供的Timer类,Timer类是一个定时器, 它有如下一个构造器 :
Timer(int delay, ActionListener listener): 每间隔 delay 毫秒,系统自动触发 ActionListener 监听器里的事件处理器方法,在方法内部我们就可以调用组件的repaint方法,完成组件重绘。
package com.one.draw;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class PinBall {
//创建窗口对象
private Frame frame = new Frame("弹球游戏");
//桌面宽度
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 ballX = 120;
private int ballY = 20;
//定义变量,记录小球在x方向和y方向上分别移动的速度
private int speedY = 10;
private int speedX = 5;
//定义变量,记录球拍的坐标
private int racketX = 10;
private final int racketY = 340; //球拍只能左右移动
//定义变量,标识当前游戏是否已结束
private boolean isOver = false;
//声明一个定时器
private Timer timer;
//定义一个类,继承Canvas,充当画布
private class MyCanvas extends Canvas{
@Override
public void paint(Graphics g) {
//TODO 在这绘制内容
if(isOver){
//游戏结束
//颜色
g.setColor(Color.BLUE);
//字体
g.setFont(new Font("Time", 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(racketX, racketY, RACKET_WIDTH, RACKET_HEIGHT);
}
}
}
//创建一个绘画区域
MyCanvas drawArea = new MyCanvas();
public void init(){
//组装视图,以及游戏逻辑的控制
//完成球拍坐标的变化
//创建一个监听器对象
KeyListener listener = new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
//获取当前按下的键
int keyCode = e.getKeyCode();
if(keyCode == KeyEvent.VK_LEFT){
//<-- 应该向左移动
if(racketX > 0){
racketX -= 10;
}
}
if(keyCode == KeyEvent.VK_RIGHT){
//--> 应该向右移动
//说明:此次就是要比(桌面宽 - 球拍宽)小
if(racketX < (TABLE_WIDTH - RACKET_WIDTH)){
racketX += 10;
}
}
}
};
//给Frame和drawArea注册监听器
frame.addKeyListener(listener);
drawArea.addKeyListener(listener);
ActionListener task = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//根据边界范围,修正速度
//桌面边界 - 球大小
if(ballX <= 0 || ballX >= (TABLE_WIDTH - BALL_SiZE)){
speedX = -speedX;
}
//1&&2&&3
//1:表示小球的y坐标在球拍上
//2+3:表示一个区间范围:左边大于拍横坐标,右边小于球拍的横坐标加宽度(此处横坐标表示左端点)
if(ballY <= 0 || (ballY > racketY - BALL_SiZE && ballX > racketX && ballX < racketX + RACKET_WIDTH)){
speedY = -speedY;
}
//在球拍之下,且不在球拍之内
if(ballY > racketY - BALL_SiZE && (ballX < racketX || ballX > racketX + RACKET_WIDTH)){
//当前小球超出了球拍的范围,游戏结束
//停止定时器
timer.stop();
//修改游戏是否结束的标记
isOver = true;
//重绘界面
drawArea.repaint();
}
//更新小球的坐标,重绘界面
ballX += speedX;
ballY += speedY;
//重绘界面
drawArea.repaint();
}
};
//小球坐标的控制
timer = new Timer(100, task);
timer.start();
//设置画布大小,并添加
drawArea.setPreferredSize(new Dimension(TABLE_WIDTH,TABLE_HEIGHT));
frame.add(drawArea);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args){
new PinBall().init();
}
}
3、处理位图:
如果仅仅绘制一些简单的几何图形,程序的图形效果依然比较单调 。 AWT 也允许在组件上绘制位图, Graphics 提供了 drawlmage(Image image) 方法用于绘制位图,该方法需要一个Image参数一一代表位图,通过该方法就可以绘制出指定的位图 。
位图使用步骤:
1.创建Image的子类对象BufferedImage(int width,int height,int ImageType),创建时需要指定位图的宽高及类型属性;此时相当于在内存中生成了一张图片;
2.调用BufferedImage对象的getGraphics()方法获取画笔,此时就可以往内存中的这张图片上绘图了,绘图的方法和之前学习的一模一样;
3.调用组件paint方法中提供的Graphics对象的的drawImage()方法,一次性的内存中的图片BufferedImage绘制到特定的组件上。
使用位图绘制组件的好处(可以类比文件缓冲区):
使用位图来绘制组件,相当于实现了图的缓冲区,此时绘图时没有直接把图形绘制到组件上,而是先绘制到内存中的BufferedImage上,等全部绘制完毕,再一次性的图像显示到组件上即可,这样用户的体验会好一些。
参考代码:
package com.one.draw;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
public class HandDraw {
//定义窗口对象
private Frame frame = new Frame("简单手绘程序");
//定义画图区域的宽高
private final int AREA_WIDTH = 500;
private final int AREA_HEIGHT = 400;
//定义一个右键菜单,用于设置画笔颜色
private PopupMenu colorMenu = new PopupMenu(); //右键菜单组件
//菜单项组件
private MenuItem redItem = new MenuItem("红色");
private MenuItem greenItem = new MenuItem("绿色");
private MenuItem blueItem = new MenuItem("蓝色");
//定义一个变量,记录当前画笔的颜色
private Color forceColor = Color.BLACK;
//创建一个BufferedImage位图对象
BufferedImage image = new BufferedImage(AREA_WIDTH, AREA_HEIGHT, BufferedImage.TYPE_INT_BGR);
//通过位图,获取关联的Graphics对象
Graphics g = image.getGraphics();
//自定义一个类,继承Canvas
private class MyCanvas extends Canvas{
@Override
public void paint(Graphics g) {
g.drawImage(image, 0, 0, null);
}
}
MyCanvas drawArea = new MyCanvas();
//定义变量,记录鼠标拖动过程中,上一次所处的坐标
private int preX = -1;
private int preY = -1;
public void init(){
//组装视图
//定义一个监听器对象,用于点击菜单后实现效果
ActionListener listener = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String actionCommand = e.getActionCommand();
//更具点击的按钮来设置对应的画笔颜色
switch(actionCommand){
case "红色":
forceColor = Color.RED;
break;
case "绿色":
forceColor = Color.GREEN;
break;
case "蓝色":
forceColor = Color.BLUE;
break;
}
}
};
//注册监听
redItem.addActionListener(listener);
greenItem.addActionListener(listener);
blueItem.addActionListener(listener);
//添加菜单项到菜单
colorMenu.add(redItem);
colorMenu.add(greenItem);
colorMenu.add(blueItem);
//把colorMenu菜单添加到绘图区域
drawArea.add(colorMenu);
//为绘图区域设置鼠标监听器
drawArea.addMouseListener(new MouseAdapter() {
@Override
public void mouseReleased(MouseEvent e) { //当鼠标键抬起时被调用
boolean popupTrigger = e.isPopupTrigger();
if(popupTrigger){ //如果时点击的鼠标右键,就让菜单显示出来
//并表示显示在何处,坐标通过方法进行获取
colorMenu.show(drawArea, e.getX(), e.getY());
}
//重置preX和preY
preX = -1;
preY = -1;
}
});
//设置位图的背景为白色
g.setColor(Color.WHITE);
g.fillRect(0, 0, AREA_WIDTH, AREA_HEIGHT); //并填充好
//通过监听鼠标的移动,完成线条的绘制
drawArea.addMouseMotionListener(new MouseMotionAdapter() {
//该方法,当鼠标左键按下,并进行拖动时,会被调用
@Override
public void mouseDragged(MouseEvent e) {
if(preX > 0 && preY > 0){
g.setColor(forceColor);
//画线条
//两点构成一条直线,一个是起点,一个是终点(使用e.getX e.getY)
g.drawLine(preX, preY, e.getX(), e.getY());
}
//修正preX和preY的值
preX = e.getX();
preY = e.getY();
//重绘组件
drawArea.repaint();
}
});
drawArea.setPreferredSize(new Dimension(AREA_WIDTH, AREA_HEIGHT));
frame.add(drawArea);
//设置frame最佳大小并可见
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args){
new HandDraw().init();
}
}
4、ImageIO的使用:
在实际生活中,很多软件都支持打开本地磁盘已经存在的图片,然后进行编辑,编辑完毕后,再重新保存到本地磁盘。如果使用AWT要完成这样的功能,那么需要使用到ImageIO这个类,可以操作本地磁盘的图片文件。
方法名称 | 方法功能 |
---|---|
static BufferedImage read(File input) | 读取本地磁盘图片文件 |
static BufferedImage read(InputStream input) | 读取本地磁盘图片文件 |
static boolean write(RenderedImage im, String formatName, File output) | 往本地磁盘中输出图片文件 |
参考代码:
package com.one.draw;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
public class ReadAndSaveImage {
private Frame frame = new Frame("图片查看器");
//创建菜单条
MenuBar menuBar = new MenuBar();
//创建菜单
Menu menu = new Menu("文件");
//创建菜单项
MenuItem open = new MenuItem("打开");
MenuItem save = new MenuItem("另存为");
//声明BufferedImage对象,记录本地存取到内存中的图片
BufferedImage image;
private class MyCanvas extends Canvas{
@Override
public void paint(Graphics g) {
g.drawImage(image, 0, 0, null);
}
}
MyCanvas drawArea = new MyCanvas();
public void init() throws Exception{
//组装视图
open.addActionListener(e->{
//打开一个文件对话框
FileDialog fileDialog = new FileDialog(frame, "打开图片", FileDialog.LOAD);
fileDialog.setVisible(true);
//获取用户选择的图片路径以及名称
String dir = fileDialog.getDirectory();
String fileName = fileDialog.getFile();
//调用ImageIO.read()方法
try {
image = ImageIO.read(new File(dir, fileName)); //传入了一个文件对象
drawArea.repaint(); //重绘
} catch (IOException ioException) {
ioException.printStackTrace();
}
});
save.addActionListener(e->{
//展示一个文件对话框
FileDialog fileDialog = new FileDialog(frame, "保存图片", FileDialog.SAVE);
fileDialog.setVisible(true);
//获取用户设置的保存路径以及文件名称
String dir = fileDialog.getDirectory();
String fileName = fileDialog.getFile();
//调用ImageIO.write()方法
try {
ImageIO.write(image, "JPEG", new File(dir, fileName));
} catch (IOException ioException) {
ioException.printStackTrace();
}
});
menu.add(open);
menu.add(save);
menuBar.add(menu);
//把菜单条放入到窗口中
frame.setMenuBar(menuBar);
frame.add(drawArea);
frame.setBounds(200, 200, 740, 506);
frame.setVisible(true);
//监听窗口关闭的动作
frame.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
public static void main(String[] args) throws Exception{
new ReadAndSaveImage().init();
}
}
5、五子棋游戏:
参考代码:
package com.one.draw;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.File;
//图片来源于:本模块img中
//为了得到更好的展示效果,此处使用了swing中的两个类
public class Gobang {
//定义五子棋游戏入口
private JFrame f = new JFrame("五子棋游戏"); //此处使用了swing中的组件
//声明四个BufferedImage对象,分别记录四张图片
BufferedImage table;
BufferedImage black;
BufferedImage white;
BufferedImage selected;
//声明棋盘的宽和高
final int TABLE_WIDTH = 535;
final int TABLE_HEIGHT = 536;
//声明棋盘横向和纵向分别可以下多少子,他们的值都为15
final int BOARD_SIZE = 15;
//需要一个变量声明每一个棋子占用棋盘的比率
final int RATE = TABLE_WIDTH / BOARD_SIZE; //棋盘宽除以棋子个数
//声明变量,记录对于棋子x方向和y方向的偏移量(更具棋盘差来的)
final int X_OFFSET = 5;
final int Y_OFFSET = 6;
//声明一个二维数组,记录棋子,如果值为 0就是无棋子 1 - 白棋 2 - 黑棋
int[][] board = new int[BOARD_SIZE][BOARD_SIZE];
//声明红色选择框的坐标,该坐标其实就是二维数组board中的索引
int selected_X = -1;
int selected_Y = -1;
//自定义一个类继承Canvas,实现画布
private class ChessBoard extends JPanel {
@Override
public void paint(Graphics g){
//绘图
//绘制棋盘
g.drawImage(table, 0, 0, null);
//绘制选择框 加上偏移量
if(selected_X > 0 &&selected_Y > 0){ //选择框的坐标合理时进行绘制
//selected_X * RATE 是为了把后续处理修正回来
g.drawImage(selected, selected_X*RATE + X_OFFSET, selected_Y*RATE + Y_OFFSET, null);
}
//绘制棋子
for(int i = 0; i < BOARD_SIZE; i++){
for(int j = 0; j < BOARD_SIZE; j++){
//绘制棋子
//绘制黑棋
if(board[i][j] == 2){
g.drawImage(black, (i*RATE) + X_OFFSET, (j*RATE) + Y_OFFSET ,null);
}
//绘制白棋
if(board[i][j] == 1){
g.drawImage(white, (i*RATE) + X_OFFSET, (j*RATE) + Y_OFFSET ,null);
}
}
}
}
}
ChessBoard chessBoard = new ChessBoard();
//声明变量,记录当前下棋的颜色
int board_type = 2;
//声明底部需要用到的组件
Panel p = new Panel();
Button whiteBtn = new Button("白棋");
Button blackBtn = new Button("黑棋");
Button deleteBtn = new Button("删除");
//定义一个方法刷新按钮的颜色 (及选中的按钮会变色)
/**
*
* @param whiteBtnColor 白色按钮颜色
* @param blackBtnColor 黑色按钮颜色
* @param deleteBtnColor 删除按钮颜色
*/
public void refreshBtnColor(Color whiteBtnColor, Color blackBtnColor, Color deleteBtnColor){
whiteBtn.setBackground(whiteBtnColor);
blackBtn.setBackground(blackBtnColor);
deleteBtn.setBackground(deleteBtnColor);
}
public void init() throws Exception{
//组装视图,编写逻辑
//组装底部按钮
//先注册监听
whiteBtn.addActionListener(e->{
//修改当前要下的棋子的标志为1
board_type = 1;
//刷新按钮颜色
refreshBtnColor(Color.GREEN, Color.GRAY, Color.GRAY);
});
blackBtn.addActionListener(e->{
//修改当前要下的棋子的标志为2
board_type = 2;
//刷新按钮颜色
refreshBtnColor(Color.GRAY, Color.GREEN, Color.GRAY);
});
deleteBtn.addActionListener(e->{
//修改当前要下的棋子的标志为0
board_type = 0;
//刷新按钮颜色
refreshBtnColor(Color.GRAY, Color.GRAY, Color.GREEN);
});
//添加按钮到Panel组件(默认流式布局)中
p.add(whiteBtn);
p.add(blackBtn);
p.add(deleteBtn);
f.add(p, BorderLayout.SOUTH); //将Panel组件放到frame的南边
//给四个BufferedImage对象赋值
table = ImageIO.read(new File("awt\\img\\board.jpg"));
white = ImageIO.read(new File("awt\\img\\white.gif"));
black = ImageIO.read(new File("awt\\img\\black.gif"));
selected = ImageIO.read(new File("awt\\img\\selected.gif"));
//组装棋盘
//处理鼠标移动
chessBoard.addMouseMotionListener(new MouseMotionAdapter() {
//当鼠标移动时会调用该方法
@Override
public void mouseMoved(MouseEvent e) {
//将鼠标坐标转换为二维数组坐标
selected_X = (e.getX()-X_OFFSET) / RATE;
selected_Y = (e.getY()-Y_OFFSET) / RATE;
//重绘界面
chessBoard.repaint();
}
});
//处理鼠标点击
chessBoard.addMouseListener(new MouseAdapter() {
//当鼠标被点击后
@Override
public void mouseClicked(MouseEvent e) {
int xPos = (e.getX()-X_OFFSET) / RATE;
int yPos = (e.getY()-Y_OFFSET) / RATE;
board[xPos][yPos] = board_type;
//重绘界面
chessBoard.repaint();
}
//当鼠标移除界面外时
@Override
public void mouseExited(MouseEvent e){
selected_X = -1;
selected_Y = -1;
chessBoard.repaint();
}
});
//设置棋盘大小
chessBoard.setPreferredSize(new Dimension(TABLE_WIDTH, TABLE_HEIGHT));
f.add(chessBoard);
//给 X 注册监听
f.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
//设置frame最佳大小值
f.pack();
f.setVisible(true);
}
public static void main(String[] args) throws Exception{
new Gobang().init();
}
}