欢迎光临
我们一直在努力

绘制象棋棋盘 | Java界面实战三

绘制象棋棋盘 | Java界面实战三

  • 继承
    • 1. 继承的概念
    • 2. 继承的特点
    • 3. 继承的格式
    • 4. 继承的说明
    • 5. 在界面中的运用
    • 6. 创建界面
  • Graphics 绘制
    • 1. 画直线(drawLine)
    • 2. 绘制矩形
    • 3. 绘制椭圆与圆弧
    • 4. 绘制多边形
    • 5. 绘制文字
    • 6. 设置颜色与字体
    • 7. 绘制图像
  • 棋盘绘制
    • 1. 网格线
    • 2. 楚河汉界
    • 3. 棋盘美化:背景图片与防遮挡
    • 4. 绘制棋子
  • 完整源码整合:GoUI、MyFrame、XQ 三合一
    • GoUI类
    • MyFrame类
    • XQ类

继承

在正式开始之前,我们需要掌握继承的相关知识

1. 继承的概念

继承是面向对象编程中的重要特性。子类通过继承父类,可以获得父类的属性和方法,并在此基础上进行扩展和改造。

  • 接口:通过一个类实现接口,类中必须重写接口中的方法。
  • 类继承:通过继承另一个类,也可以重写这个类的方法。

2. 继承的特点

  • 可以继承另一个类的属性和方法。
  • 可以重写这个类的方法,进行扩展和改造。
  • 3. 继承的格式

    class 子类名 extends 父类名 {
    // 子类内容
    }

    4. 继承的说明

    • 子类可以替代父类。
    • 子类基于父类方法进行扩展。

    5. 在界面中的运用

    在编写带有图形界面的游戏时,我们会创建一个自己的类,让它继承 JFrame,从而拥有窗体的基本能力。

    public class MyFrame extends JFrame {
    // 示例:继承 JFrame 后,可以重写窗体的刷新方法
    // 重写paint方法 窗体被改变尺寸时 会自动调用
    @Override
    public void paint(Graphics g) {
    super.paint(g);// 绘制原本的窗体中的白板
    // 在这里添加需要绘制到界面上的内容
    }
    }

    程序讲解

  • public class MyFrame extends JFrame

    定义一个自己的窗体类 MyFrame,并通过 extends 继承 JFrame。这样 MyFrame 就直接拥有了窗体的基本能力,例如标题栏、可调整大小、可关闭等。

  • @Override

    @Override 是一个注解,表示下面的 paint 方法不是新写一个普通方法,而是在重写父类已有的方法。加上它以后,如果我们把方法名写错,例如写成 Paint,编译器会直接提示错误,帮我们提前发现问题。

  • public void paint(Graphics g)

    paint 是窗体需要重新绘制时自动调用的方法。例如:

    • 窗体第一次显示时;
    • 窗体大小被改变时;
    • 窗体被遮挡后又重新显示时。

    Graphics g 可以理解为一支“画笔”,后面要在界面上画棋盘、画游戏页面,主要就是通过这个 g 来完成。

  • super.paint(g);

    super 代表父类对象。这里调用父类的 paint 方法,意思是:先让 JFrame 完成它原本该做的绘制工作,再执行我们自己添加的绘制内容。

    如果不写 super.paint(g);,父类原有的基础绘制逻辑可能不会执行,窗口看起来会不完整,例如背景没有被正常刷新。

  • 自定义绘制内容

    在注释 // 在这里添加需要绘制到界面上的内容 的位置,就可以使用 g 来绘制自己的内容,例如:

    g.drawRect(x, y, width, height); // 画矩形
    g.drawString("游戏开始", x, y); // 画文字

    棋盘、棋子、游戏页面等都可以通过这一步骤绘制出来。

  • 6. 创建界面

    package Frame0813;

    import javax.swing.*;

    public class GoUI {
    public void showUI(){
    MyFrame jf = new MyFrame();
    jf.setTitle("象棋");
    jf.setSize(900, 900);
    jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    jf.setVisible(true);
    }
    static void main(){
    GoUI ui = new GoUI();
    ui.showUI();
    }
    }

    在代码中,重点关注这一句:MyFrame jf = new MyFrame();。

    在实战一、二中,我们写的都是 JFrame jf = new JFrame();,为什么这里要换成 MyFrame 呢?主要原因有两点:

    • MyFrame 继承自 JFrame,所以它本质上仍然是一个窗体,JFrame 原有的方法都可以继续使用。
    • MyFrame 中重写了 paint(Graphics g) 方法。只有创建 MyFrame 对象,窗体刷新时才会执行我们自定义的绘制逻辑;如果仍然创建 JFrame jf = new JFrame();,就不会调用 MyFrame 中的 paint 方法,后面也就无法在窗体上画出棋盘和游戏页面。

    因此,MyFrame jf = new MyFrame(); 可以理解为:创建了一个“带自定义绘制能力”的窗体。

    运行效果:

    在这里插入图片描述

    Graphics 绘制

    了解完接口后,我们再来看一看 Graphics 绘制。Graphics 可以理解为前面 paint(Graphics g) 中的那支“画笔”,接下来画直线、矩形、文字以及棋盘,都要依靠它来完成。

    1. 画直线(drawLine)

    • drawLine(int x1, int y1, int x2, int y2):画直线,从 (x1, y1) 到 (x2, y2)。

    g.drawLine(10, 10, 100, 100);

    在这里插入图片描述

    2. 绘制矩形

    • drawRect(int x, int y, int width, int height):画空心矩形,x,y 是左上角坐标。
    • fillRect(int x, int y, int width, int height):填充实心矩形。

    g.drawRect(50, 50, 200, 100); // 空心矩形
    g.fillRect(300, 50, 200, 100); // 实心矩形

    在这里插入图片描述

    3. 绘制椭圆与圆弧

    • drawOval(int x, int y, int w, int h):画椭圆;当 w = h 时就是圆。
    • fillOval(int x, int y, int w, int h):填充实心椭圆或圆。
    • drawArc(int x, int y, int w, int h, int startAngle, int arcAngle):画圆弧。
    • fillArc(int x, int y, int w, int h, int startAngle, int arcAngle):填充扇形。

    注:在画圆弧与扇形时,startAngle 表示起始角度,arcAngle 表示从起始角度开始扫过的角度。在 Java 的坐标系中,0° 指向右侧,角度沿逆时针方向增加。

    g.drawOval(50, 200, 100, 100); // 正圆
    g.fillOval(200, 200, 100, 60); // 实心椭圆
    g.drawArc(350, 200, 100, 100, 0, 180); // 半圆弧
    g.fillArc(500, 200, 100, 100, 0, 90); // 扇形

    在这里插入图片描述

    说明:矩形和椭圆的 x,y 坐标都表示它们“外接矩形”的左上角位置。因此,只要让矩形和椭圆的 x,y,w,h 保持一致,就能画出一个矩形的内切椭圆;当 w = h 时,这个内切椭圆就变成了矩形的内切圆。

    // 先画矩形,再画出它的内切圆
    g.drawRect(50, 50, 100, 100);
    g.drawOval(50, 50, 100, 100);

    在这里插入图片描述

    4. 绘制多边形

    • drawPolygon(int[] xPoints, int[] yPoints, int nPoints):画多边形。
    • fillPolygon(int[] xPoints, int[] yPoints, int nPoints):填充多边形。

    参数说明:

    • xPoints:多边形所有顶点的 x 坐标数组。
    • yPoints:多边形所有顶点的 y 坐标数组;xPoints[i] 与 yPoints[i] 共同确定第 i 个顶点。
    • nPoints:实际参与绘制的顶点数量,通常等于数组长度。如果数组较长但只想使用前面几个顶点,可以传入更小的数值。

    绘制时,程序会按照顶点顺序依次连线,最后自动连接首尾顶点,形成封闭的多边形。

    下面再通过两个例子加深理解。

    示例 1:绘制正五边形

    // 1. 绘制正五边形:5 个顶点均匀分布在圆周上,相邻顶点夹角 72°
    int[] xs = {150, 207, 185, 115, 93};
    int[] ys = {340, 381, 449, 449, 381};
    g.drawPolygon(xs, ys, 5);

    请添加图片描述

    示例 2:小房子

    // 2. 先画三角形屋顶
    int[] roofX = {150, 200, 250};
    int[] roofY = {300, 250, 300};
    g.drawPolygon(roofX, roofY, 3);

    // 再画四边形墙体
    int[] bodyX = {160, 160, 240, 240};
    int[] bodyY = {300, 350, 350, 300};
    g.drawPolygon(bodyX, bodyY, 4);

    这里通过组合两个多边形,就能画出一个小房子。实际项目中很多复杂图形也是这样由基础多边形拼接出来的。 在这里插入图片描述

    5. 绘制文字

    • drawString(String str, int x, int y):绘制文字,x,y 是文字左下角位置。

    g.drawString("Hello Java", 20, 500);

    6. 设置颜色与字体

    • setColor(Color c):设置画笔颜色。
    • setFont(Font font):设置字体。

    g.setColor(Color.RED);
    g.setFont(new Font("宋体", Font.BOLD, 20));

    三个结合的效果:

    g.setColor(new Color(12,123,25));
    g.setFont(new Font("宋体", Font.BOLD, 30));
    g.drawString("Hello Java", 20, 500);

    在这里插入图片描述

    7. 绘制图像

    • ImageIcon(String filename):根据文件路径加载一张图片,生成 ImageIcon 对象。
    • getImage():从 ImageIcon 对象中取出真正用于绘制的 Image 图片。
    • drawImage(Image img, int x, int y, int width, int height, ImageObserver observer):把图片绘制到界面上。
      • x,y:图片左上角在窗口中的坐标。
      • width,height:图片绘制后的宽度和高度;如果不写这两个参数,则按图片原本大小绘制。
      • observer:通常直接写 null。

    ImageIcon img = new ImageIcon("D:\\\\CodeTeach\\\\Java\\\\chinesechess1.png");
    g.drawImage(img.getImage(), 0, 0, 900, 900, null);

    代码说明

    • new ImageIcon("…") 用来加载本地电脑上的棋盘背景图片;路径中的 \\\\ 是 Java 字符串里 \\ 的转义写法。
    • img.getImage() 取出 ImageIcon 中的 Image 对象,再交给 g 去绘制。
    • g.drawImage(img.getImage(), 0, 0, 900, 900, null) 表示从窗口左上角 (0, 0) 开始,把图片绘制成 900 × 900 的大小。
    • 如果图片路径写错或文件不存在,图片会加载失败,界面上就看不到背景图;实际使用时需要先确认图片位置是否正确。

    效果: 在这里插入图片描述

    棋盘绘制

    1. 网格线

    在画之前,我们需要先定义一批绘制棋盘时要用到的常量。这样后面画网格线、画棋子时都可以直接使用这些变量,统一修改位置和大小也会更加方便。

    • X = 80:棋盘左上角的 x 坐标,决定棋盘在窗口中的横向起点。
    • Y = 100:棋盘左上角的 y 坐标,决定棋盘在窗口中的纵向起点。
    • SIZE = 80:每个格子的间距,也就是相邻两条线之间的距离。SIZE 是棋盘绘制的关键参数,后面调整棋盘大小主要就是修改它。
    • ROW = 9:棋盘的行数,表示横向排列的行数。
    • COL = 8:棋盘的列数,表示纵向排列的列数。

    // 棋盘绘制参数
    int X = 80; // 棋盘左上角 x 坐标
    int Y = 100; // 棋盘左上角 y 坐标
    int SIZE = 80; // 每个格子的间距
    int ROW = 9; // 棋盘行数
    int COL = 8; // 棋盘列数

    接着,我们使用 for 循环来画网格线。网格线的本质,就是由多条横线和竖线交叉组成的:因此,我们只需要分别控制每条横线、竖线的位置,就能用循环批量绘制出完整棋盘。

    package Frame0813;

    import javax.swing.*;
    import java.awt.*;

    public class MyFrame extends JFrame {
    int X = 80,Y=100,SIZE=80,ROW=9,COL=8;

    @Override
    public void paint(Graphics g) {
    super.paint(g);
    for (int i = 0; i <=ROW; i++) {
    g.drawLine(X,Y+i*SIZE,X+COL*SIZE,Y+i*SIZE);
    }
    for (int i = 0; i <=COL; i++) {
    g.drawLine(X+i*SIZE,Y,X+i*SIZE,Y+ROW*SIZE);
    }
    }
    }

    在这里插入图片描述

    2. 楚河汉界

    象棋棋盘中间是楚河汉界,通常不需要竖线,而是一段空白区域。绘制时,我们可以先画一个白色矩形,把中间多余的网格线覆盖住,让棋盘中央自然形成楚河汉界。

    g.setColor(Color.WHITE);
    g.fillRect(X, Y+4*SIZE, (ROW1)*SIZE, SIZE);

    在这里插入图片描述 最后,在白色矩形上绘制“楚河汉界”四个字:

    g.setColor(Color.BLACK);
    String str1="楚",str2="河",str3="汉",str4="界";
    Font font = new Font("微软雅黑",Font.BOLD,60);
    g.setFont(font);
    g.drawString(str1,X+SIZE,Y+SIZE*520);
    g.drawString(str2,X+SIZE*2,Y+SIZE*520);
    g.drawString(str3,X+SIZE*5,Y+SIZE*520);
    g.drawString(str4,X+SIZE*6,Y+SIZE*520);

    在这里插入图片描述 除了楚河汉界,象棋棋盘上下两端还各有一个九宫格。我们还需要在上下两个九宫格中各画两条交叉的斜线,组成“米”字格。九宫格中的斜线主要用于标出“士”的行走路线:士每次只能沿着这些斜线移动一格,因此画好斜线后,九宫格区域才完整。

    g.drawLine(X+SIZE*3,Y,X+SIZE*5,Y+SIZE*2);
    g.drawLine(X+SIZE*5,Y,X+SIZE*3,Y+SIZE*2);
    g.drawLine(X+SIZE*3,Y+SIZE*9,X+SIZE*5,Y+SIZE*7);
    g.drawLine(X+SIZE*3,Y+SIZE*7,X+SIZE*5,Y+SIZE*9);

    请添加图片描述

    3. 棋盘美化:背景图片与防遮挡

    棋盘绘制完成后,整体略显单调。这里分两步处理:

  • 添加背景图片:加载本地图片并绘制到窗口,让界面更美观。
  • 绘制橙色底板:在棋盘下方先垫一层橙色矩形,既能突出棋盘,又能防止背景图干扰网格线。
  • ImageIcon img = new ImageIcon("D:\\\\CodeTeach\\\\Java\\\\chinesechess1.png");
    g.drawImage(img.getImage(), 0, 0, 900, 900, null);

    在这里插入图片描述

    g.setColor(Color.ORANGE);
    g.fillRect(XSIZE/2,YSIZE/2,SIZE*COL+SIZE,ROW*SIZE+SIZE);

    在这里插入图片描述

    4. 绘制棋子

    棋子数量较多,逐个手写会非常麻烦。这里改用二维数组保存棋子布局,再通过循环统一遍历绘制。

    更多 Java 数组与 switch 语句讲解,可参考:Java数组相关知识

    先定义棋盘参数和初始棋子数组。数组中的数字表示棋子类型,0 表示空位:

    public class XQ {
    int X = 80,Y=100,SIZE=80,ROW=9,COL=8;
    int[][] initChessArr = {
    {5, 4, 3, 2, 1, 2, 3, 4, 5},
    {0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 6, 0, 0, 0, 0, 0, 6, 0},
    {7, 0, 7, 0, 7, 0, 7, 0, 7},
    {0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 0}
    };
    }

    接着用双层循环遍历数组,读取每个位置的值;值不为 0 时,表示这里有棋子,调用 drawChess 进行绘制:

    // 棋盘初始化棋子
    public void initChess(Graphics g) {
    for (int i = 0; i < 10; i++) {
    for (int j = 0; j < 9; j++) {
    int num = initChessArr[i][j];
    if (num != 0) {
    drawChess(g,num,i,j);
    }
    }
    }
    }

    drawChess 方法负责绘制单个棋子:

  • 通过 switch 把数组中的数字转换为对应棋子名称;
  • 根据行列位置计算棋子圆形的左上角坐标;
  • 先画绿色实心圆作为棋子,再在圆上写黑色文字。
  • public void drawChess(Graphics g, int num,int i,int j) {
    String str = "";
    switch (num) {
    case 1 -> str = "帅";
    case 2 -> str = "仕";
    case 3 -> str = "相";
    case 4 -> str = "车";
    case 5 -> str = "马";
    case 6 -> str = "炮";
    case 7 -> str = "兵";
    }
    int cx = j*SIZE+XSIZE/2;
    int cy = i*SIZE+YSIZE/2;
    g.setColor(Color.GREEN);
    g.fillOval(cx,cy,SIZE,SIZE);
    g.setColor(Color.BLACK);
    Font font = new Font("微软雅黑", Font.BOLD, 45);
    g.setFont(font);
    g.drawString(str,cx+SIZE/6,cy+55);
    }

    最后,在 paint 方法中创建 XQ 对象并调用棋子绘制方法:

    XQ xq = new XQ();
    xq.initChess(g);// 绘制棋子

    效果: 在这里插入图片描述 当然,你也可以不画棋盘板子: 在这里插入图片描述

    完整源码整合:GoUI、MyFrame、XQ 三合一

    GoUI类

    package Frame0813;

    import javax.swing.*;

    public class GoUI {
    public void showUI(){
    MyFrame jf = new MyFrame();
    jf.setTitle("象棋 ");
    jf.setSize(900, 900);
    jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    jf.setVisible(true);
    }
    static void main(){
    GoUI ui = new GoUI();
    ui.showUI();
    }
    }

    MyFrame类

    package Frame0813;

    import javax.swing.*;
    import java.awt.*;

    public class MyFrame extends JFrame {
    int X = 80,Y=100,SIZE=80,ROW=9,COL=8;

    @Override
    public void paint(Graphics g) {
    super.paint(g);
    // 背景图
    ImageIcon img = new ImageIcon("D:\\\\CodeTeach\\\\Java\\\\chinesechess1.png");
    g.drawImage(img.getImage(), 0, 0, 900, 900, null);
    // 棋盘板子
    g.setColor(Color.ORANGE);
    g.fillRect(XSIZE/2,YSIZE/2,SIZE*COL+SIZE,ROW*SIZE+SIZE);

    // 网格线
    g.setColor(Color.BLACK);
    for (int i = 0; i <=ROW; i++) {
    g.drawLine(X,Y+i*SIZE,X+COL*SIZE,Y+i*SIZE);
    }
    for (int i = 0; i <=COL; i++) {
    g.drawLine(X+i*SIZE,Y,X+i*SIZE,Y+ROW*SIZE);
    }
    // 楚河汉界
    g.setColor(Color.WHITE);
    g.fillRect(X, Y+4*SIZE, (ROW1)*SIZE, SIZE);

    g.setColor(Color.BLACK);
    String str1="楚",str2="河",str3="汉",str4="界";
    Font font = new Font("微软雅黑",Font.BOLD,60);
    g.setFont(font);
    g.drawString(str1,X+SIZE,Y+SIZE*520);
    g.drawString(str2,X+SIZE*2,Y+SIZE*520);
    g.drawString(str3,X+SIZE*5,Y+SIZE*520);
    g.drawString(str4,X+SIZE*6,Y+SIZE*520);

    // 九宫格
    g.drawLine(X+SIZE*3,Y,X+SIZE*5,Y+SIZE*2);
    g.drawLine(X+SIZE*5,Y,X+SIZE*3,Y+SIZE*2);
    g.drawLine(X+SIZE*3,Y+SIZE*9,X+SIZE*5,Y+SIZE*7);
    g.drawLine(X+SIZE*3,Y+SIZE*7,X+SIZE*5,Y+SIZE*9);

    // 绘制棋子
    XQ xq = new XQ();
    xq.initChess(g);
    }
    }

    XQ类

    package Frame0813;

    import java.awt.*;

    public class XQ {
    int X = 80,Y=100,SIZE=80,ROW=9,COL=8;
    int[][] initChessArr = {
    {5, 4, 3, 2, 1, 2, 3, 4, 5},
    {0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 6, 0, 0, 0, 0, 0, 6, 0},
    {7, 0, 7, 0, 7, 0, 7, 0, 7},
    {0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 0},
    {0, 0, 0, 0, 0, 0, 0, 0, 0}
    };
    // 棋盘初始化棋子
    public void initChess(Graphics g) {
    for (int i = 0; i < 10; i++) {
    for (int j = 0; j < 9; j++) {
    int num = initChessArr[i][j];
    if (num != 0) {
    drawChess(g,num,i,j);
    }
    }
    }
    }
    public void drawChess(Graphics g, int num,int i,int j) {
    String str = "";
    switch (num) {
    case 1 -> str = "帅";
    case 2 -> str = "仕";
    case 3 -> str = "相";
    case 4 -> str = "车";
    case 5 -> str = "马";
    case 6 -> str = "炮";
    case 7 -> str = "兵";
    }
    int cx = j*SIZE+XSIZE/2;
    int cy = i*SIZE+YSIZE/2;
    g.setColor(Color.GREEN);
    g.fillOval(cx,cy,SIZE,SIZE);
    g.setColor(Color.BLACK);
    Font font = new Font("微软雅黑", Font.BOLD, 45);
    g.setFont(font);
    g.drawString(str,cx+SIZE/6,cy+55);
    }
    }

    赞(0)
    未经允许不得转载:171主机测评 » 绘制象棋棋盘 | Java界面实战三
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址