2.7 菜单组件
前面讲解了如果构建GUI界面,其实就是把一些GUI的组件,按照一定的布局放入到容器中展示就可以了。在实际开发中,除了主界面,还有一类比较重要的内容就是菜单相关组件,可以通过菜单相关组件很方便的使用特定的功能,在AWT中,菜单相关组件的使用和之前学习的组件是一模一样的,只需要把菜单条、菜单、菜单项组合到一起,按照一定的布局,放入到容器中即可。
下表中给出常见的菜单相关组件:
菜单组件名称 | 功能 |
---|---|
MenuBar | 菜单条 , 菜单的容器 。 |
Menu | 菜单组件 , 菜单项的容器 。 它也是Menultem的子类 ,所以可作为菜单项使用 |
PopupMenu | 上下文菜单组件(右键菜单组件) |
Menultem | 菜单项组件 。 |
CheckboxMenuItem | 复选框菜单项组件 |
下图是常见菜单相关组件集成体系图:
菜单相关组件使用:
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中常用菜单组件,完成下图效果
演示代码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() {
@Override
public 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实现下图效果:
实现思路:
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);
//创建一个Panel
private 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() {
@Override
public 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方法,刷新组件外观;
一般情况下,update和paint方法是由AWT系统负责调用,如果程序要希望系统重新绘制组件,可以调用repaint方法完成。
2.8.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() | 绘制位图 |
案例:
使用AWT绘图API,完成下图效果
演示代码:
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() {
@Override
public void actionPerformed(ActionEvent e) {
shape = RECT_SHAPE;
drawArea.repaint();
}
});
drawOvalBtn.addActionListener(new ActionListener() {
@Override
public 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{
@Override
public 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定时器,完成下图中弹球小游戏。
演示代码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(){
//监听键盘 ← → 按下操作,当指定的键按下时,球拍的水平坐标分别会增加或者减少
@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);
}
}
}
}
2.8.3 处理位图
如果仅仅绘制一些简单的几何图形,程序的图形效果依然比较单调 。 AWT 也允许在组件上绘制位图, Graphics 提供了 drawlmage() 方法用于绘制位图,该方法需要一个Image参数一一代表位图,通过该方法就可 以绘制出指定的位图 。
位图使用步骤:
1.创建Image的子类对象BufferedImage(int width,int height,int ImageType),创建时需要指定位图的宽高及类型属性;此时相当于在内存中生成了一张图片;
2.调用BufferedImage对象的getGraphics()方法获取画笔,此时就可以往内存中的这张图片上绘图了,绘图的方法和之前学习的一模一样;
3.调用组件的drawImage()方法,一次性的内存中的图片BufferedImage绘制到特定的组件上。
使用位图绘制组件的好处:
使用位图来绘制组件,相当于实现了图的缓冲区,此时绘图时没有直接把图形绘制到组件上,而是先绘制到内存中的BufferedImage上,等全部绘制完毕,再一次性的图像显示到组件上即可,这样用户的体验会好一些。
案例:
通过BufferedImage实现一个简单的手绘程序:通过鼠标可以在窗口中画图。
演示代码:
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(){
@Override
public 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() {
@Override
public 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);
//把右键菜单添加到绘图区域drawArea
drawArea.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() {
//用于绘制图像
@Override
public void mouseDragged(MouseEvent e) {//按下鼠标键并拖动会触发
//如果上次鼠标的坐标在绘图区域,才开始绘图
if (preX>0 && preY>0){
//设置当前选中的画笔颜色
g.setColor(forceColor);
//绘制线条,需要有两组坐标,一组是上一次鼠标拖动鼠标时的坐标,一组是现在鼠标的坐标
g.drawLine(preX,preY,e.getX(),e.getY());
}
//更新preX和preY
preX = e.getX();
preY = e.getY();
//重新绘制drawArea组件
drawArea.repaint();
}
});
drawArea.addMouseListener(new MouseAdapter() {
//用于弹出右键菜单
@Override
public void mouseReleased(MouseEvent e) {//松开鼠标键会触发
boolean popupTrigger = e.isPopupTrigger();
if (popupTrigger){
//把colorMenu显示到drawArea画图区域,并跟随鼠标显示
colorMenu.show(drawArea,e.getX(),e.getY());
}
//当鼠标松开时,把preX和preY重置为-1
preX = -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这个类,可以操作本地磁盘的图片文件。
方法名称 | 方法功能 |
---|---|
static BufferedImage read(File input) | 读取本地磁盘图片文件 |
static BufferedImage read(InputStream input) | 读取本地磁盘图片文件 |
static boolean write(RenderedImage im, String formatName, File output) | 往本地磁盘中输出图片文件 |
案例:
编写图片查看程序,支持另存操作
演示代码:
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{
@Override
public 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() {
@Override
public void windowClosing(WindowEvent e) {
System.exit(0);
}
});
}
public static void main(String[] args) throws Exception {
new ReadAndSaveImage().init();
}
}
2.8.5 五子棋
接下来,我们使用之前学习的绘图技术,做一个五子棋的游戏。
演示代码:
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中的数目是一致的,都为15
private final int BOARD_SIZE = 15;
//定义每个棋子所占棋盘总宽度的大小比率;每个棋子所占宽度 535/15=35
private 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方法,实现绘画
@Override
public 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() {
@Override
public void actionPerformed(ActionEvent e) {
chessCategory = 2;
updateBtnColor(Color.GREEN,Color.LIGHT_GRAY,Color.LIGHT_GRAY);
}
});
blackBtn.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
chessCategory=1;
updateBtnColor(Color.LIGHT_GRAY,Color.GREEN,Color.LIGHT_GRAY);
}
});
clearBtn.addActionListener(new ActionListener() {
@Override
public 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() {
//鼠标单击会触发
@Override
public void mouseClicked(MouseEvent e) {
//将用户鼠标的坐标,转换成棋子的坐标
int xPos = (e.getX()-X_OFFSET)/RATE;
int yPos = (e.getY()-Y_OFFSET)/RATE;
board[xPos][yPos] = chessCategory;
//重绘chessBoard
chessBoard.repaint();
}
//当鼠标退出棋盘区域后,复位选中坐标,重绘chessBoard,要保证红色选中框显示正确
@Override
public void mouseExited(MouseEvent e) {
selectX=-1;
selectY=-1;
chessBoard.repaint();
}
});
//给chessBoard注册鼠标移动监听器
chessBoard.addMouseMotionListener(new MouseMotionAdapter() {
//当鼠标移动时,修正selectX和selectY,重绘chessBoard,要保证红色选中框显示正确
@Override
public 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();
}
}