Java Swing 实战:手把手教你写一个拼图小游戏(二)
3.图片的添加与打乱
3.1图片的添加
3.1.1. 方法作用
initImage () 是拼图游戏的界面初始化核心方法
作用:
遍历 4×4 拼图网格
根据二维数组 data[i][j] 加载对应图片
使用 JLabel 展示图片并定位
将所有图片添加到游戏窗体中
3.1.2核心代码
private void initImage(){ //外循环--- for(int i= 0;i<4;i++){ for(int j = 0;j<4;j++){ //获取图片的序号 int number = data[i][j]; //创建JLabel的对象(管理容器) JLabel jLabel = new JLabel(new ImageIcon("D:\\Java项目\\Java-puzzle-game\\java-puzzle-game\\java_puzzle_game\\src\\puzzle_game\\UI\\image\\animal\\animal3\\" + number + ".jpg")); //指定图片位置 jLabel.setBounds(105*j,105*i,105,105); //把管理器添加到页面中 this.getContentPane().add(jLabel); }3.1.3 代码详解
(1)嵌套循环:遍历 4×4 网格
//外循环:控制行数 i(垂直方向) for(int i= 0;i<4;i++){ //内循环:控制列数 j(水平方向) for(int j = 0;j<4;j++){ } }(2)获取当前位置图片编号
int number = data[i][j];(3)创建图片标签 JLabel
JLabel jLabel = new JLabel(new ImageIcon("图片路径" + number + ".jpg"));详解:
JLabel:Swing 中用来显示文字 / 图片 / 图标的容器ImageIcon:加载本地图片- 拼接路径 + 图片编号,动态加载对应图片
- 这里可以提一句:路径建议使用相对路径,项目移植更方便
(4)设置图片位置与大小
jLabel.setBounds(105*j,105*i,105,105);详解:
setBounds(x, y, width, height):绝对定位
105 * j:X 坐标,每一列向右偏移 105 像素
105 * i:Y 坐标,每一行向下偏移 105 像素
105,105:图片宽高都是 105px
最终形成整齐的 4×4 网格
(5)将图片添加到界面
this.getContentPane().add(jLabel);详解:
this:代表当前游戏主界面
获取内容面板,把每个图片 JLabel 添加进去
循环结束后,界面就显示完整拼图
3.2 图片的打乱
3.2.1作用
initdata() 是拼图游戏的数据初始化方法,负责:
1.生成 0~15 连续数字(代表 16 张拼图块)
2.随机打乱顺序(让每次游戏界面都不一样)
3.将打乱后的一维数组 → 转为 4×4 二维数组
4.最终赋值给 data 数组,供界面显示使用
3.2.2核心代码
//初始化数据(打乱版) public void initdata(){ int []tempArr = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; // 打乱 Random r = new Random(); for (int i = 0; i < tempArr.length; i++) { int index = r.nextInt(tempArr.length); int temp = tempArr[i]; tempArr[i] = tempArr[index]; tempArr[index] = temp; } // ✅ 一维数组转二维数组(就这一个循环!) for (int i = 0; i < tempArr.length; i++) { data[i / 4][i % 4] = tempArr[i]; } }3.2.3代码详解
1.方法定义
public void initdata(){public:公开的,其他类也能调用这个方法
void:这个方法执行完不返回任何值
initdata:方法名,意思是 初始化数据
{}:方法体,所有代码都写在大括号里
2. 创建一个 0~15 的一维数组
int []tempArr = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};int[] tempArr:定义一个整数类型的一维数组,名字叫 tempArr(临时数组)
里面装了 0 到 15 一共 16 个数字
这是还没打乱的原始顺序
3.创建随机对象
Random r = new Random();Random:Java 自带的随机工具类
r:给随机工具起个名字
作用:后面用来生成随机下标,实现打乱数组
4. 循环打乱数组
for (int i = 0; i < tempArr.length; i++) {从数组第 0 个位置,一直遍历到最后一个位置
tempArr.length = 16
int index = r.nextInt(tempArr.length);生成一个 0 ~ 15 之间的随机数
这个随机数就是要交换的数组位置
int temp = tempArr[i]; tempArr[i] = tempArr[index]; tempArr[index] = temp;这三行是经典交换两个变量的值:
把当前位置 i 的值先存到临时变量 temp
把随机位置 index 的值放到 i
把临时变量里原来的 i 值放到 index
5. 把一维数组 → 装进 4×4 二维数组
for (int i = 0; i < tempArr.length; i++) {遍历0~15这些数字
data[i / 4][i % 4] = tempArr[i];4.事件
事件源:按钮 图片 窗体.....
事件:某些操作
如:鼠标单击,鼠标划入...
绑定监听:当事件源发生了某个事件,则执行某段代码
KeyListener 键盘监听
MouseListener 鼠标监听
ActionListener 动作监听
4.1键盘监听
4.1.1详细介绍
1.让类承诺实现键盘监听
public class GameJFrame extends JFrame implements KeyListener {implements KeyListener意思是我这个界面要监听键盘,我保证实现键盘监听的方法
这叫实现监听器接口
2.必须重写这三个方法,一个都不可以少
@Override public void keyTyped(KeyEvent e) { } @Override public void keyPressed(KeyEvent e) { } @Override public void keyReleased(KeyEvent e) { }这三个方法是系统自动调用的,不用自己调用
3.给键盘绑定键盘监听
this.addKeyListener(this);位置在 initFrame() 方法里。
给当前这个窗口(JFrame)加上键盘监听,谁来处理?我自己(this)处理。
4.在方法里写按键逻辑
1)keyPressed —— 按键按下瞬间触发
//用A键查看完整图片
@Override public void keyPressed(KeyEvent e) { int code = e.getKeyCode(); // 获取按键编号 if (code == 65) { // 65 = A // 显示全图逻辑 } }2)keyReleased —— 按键松开时触发
你把所有方向键移动、W 键通关都写在这里:
@Override public void keyReleased(KeyEvent e) { int code = e.getKeyCode(); if (code == 37) { // 左 // 移动逻辑 } else if (code == 38) { // 上 // 移动逻辑 } else if (code == 39) { // 右 } else if (code == 40) { // 下 } else if (code == 87) { // W 一键通关 } }3)keyTyped
你没用,所以空着。它只监听输入字符,不监听方向键。
4.1.2三个方法的区别
keyPressed:按下去那一刻触发
你用来:A 键看全图
keyReleased:松手那一刻触发
你用来:方向键移动、W 通关
keyTyped:输入字符时触发(a~z、1~9)
4.1.3KeyEvent e 是什么?
int code = e.getKeyCode();e:代表这次按键事件的所有信息
e.getKeyCode():获取按键编号
A = 65
W = 87
左 = 37
上 = 38
右 = 39
下 = 40
4.2鼠标监听
实现接口:MouseListener
重写 5 个方法
给组件添加监听:addMouseListener(this)
必须重写的五个方法
@Override public void mouseClicked(MouseEvent e) {} // 单击(按下+松开) @Override public void mousePressed(MouseEvent e) {} // 按下瞬间 @Override public void mouseReleased(MouseEvent e) {} // 松开瞬间 @Override public void mouseEntered(MouseEvent e) {} // 鼠标进来 @Override public void mouseExited(MouseEvent e) {} // 鼠标出去不用全部用,常用的只有 mousePressed。
e 记录了这次鼠标事件的所有信息
e.getX() → 鼠标在组件里的 X 坐标
e.getY() → 鼠标在组件里的 Y 坐标
e.getSource() → 你点的是哪个组件
e.getButton() → 点的左键 / 右键 / 滚轮
4.3动作监听
这个不常用,本项目也没有使用该方法,所以不过多介绍。
