这是一个有趣的拼图游戏!这篇文章我将逐步教你编写这个游戏的代码。首先,我们来编写游戏的整体框架。
步骤1:编写游戏的整体框架
我们将创建一个Java项目,并在其中创建以下类:
MainApp
:包含main
方法,用于启动游戏。PictureMainFrame
:继承自JFrame
,表示游戏的主界面。PictureCanvas
:继承自JPanel
,表示拼图区域,用于显示拼图小方格。PicturePreview
:继承自JPanel
,表示预览区域,用于显示原始图片。Cell
:继承自JButton
,表示拼图的每个小方格。
下面是整体框架的结构:
public class MainApp {
public static void main(String[] args) {
// 创建主界面
PictureMainFrame frame = new PictureMainFrame();
// 显示界面
frame.setVisible(true);
}
}
public class PictureMainFrame extends JFrame {
// 成员变量和构造方法
// 方法:界面初始化、添加组件、添加事件监听等
}
public class PictureCanvas extends JPanel implements MouseListener {
// 静态变量:图片ID、步数
// 成员变量:拼图小方格、空方格位置等
// 构造方法
// 方法:重新加载图片、开始游戏、方格移动等
}
public class PicturePreview extends JPanel {
// 方法:重写绘制组件方法,显示图片
}
public class Cell extends JButton {
// 构造方法:带有图片的小方格
// 方法:小方格的移动
}
在这个框架中,MainApp
负责启动游戏,PictureMainFrame
是游戏的主界面,包含拼图区和预览区,PictureCanvas
是拼图区域的实现,PicturePreview
用于显示原始图片,Cell
表示拼图的每个小方格。接下来,我们将逐步完善这些类的功能。
下一步我们将完善PictureMainFrame
类的代码,包括界面的初始化、添加组件和添加事件监听器等功能。
步骤2:完善PictureMainFrame
类
public class PictureMainFrame extends JFrame {
public static JTextField step; // 步数
private final String[] items = {"小女孩儿", "女明星"};
private JRadioButton addNumInfo; // 数字提示
private JRadioButton clearNumInfo; // 清除提示
private PictureCanvas canvas; // 拼图区
private PicturePreview preview; // 预览区
private JComboBox<String> box; // 下拉框
private JTextField name; // 图片名称
private JButton start; // 开始按钮
// 空参数构造方法
public PictureMainFrame() {
init(); // 界面初始化操作
addComponent(); // 添加组件
addPreviewImage(); // 添加预览图片与拼图图片
addActionListener(); // 为组件添加事件监听
}
// 为组件添加事件监听
private void addActionListener() {
// 数字提示
addNumInfo.addActionListener(_ -> {
canvas.reLoadPictureAddNumber();
});
// 清除提示
clearNumInfo.addActionListener(_ -> {
canvas.reLoadPictureClearNumber();
});
// 下拉框
box.addItemListener(_ -> {
int num = box.getSelectedIndex();
PictureCanvas.pictureID = num + 1;
preview.repaint();
canvas.reLoadPictureClearNumber();
name.setText("图片名称:" + box.getSelectedItem());
int stepNum = PictureCanvas.stepNum = 0;
step.setText("步数:" + stepNum);
clearNumInfo.setSelected(true);
});
// 开始按钮
start.addActionListener(_ -> {
PictureCanvas.stepNum = 0;
step.setText("步数:" + PictureCanvas.stepNum);
canvas.start();
});
}
// 添加预览图片与拼图图片
private void addPreviewImage() {
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(1, 2));
canvas = new PictureCanvas();
canvas.setBorder(new TitledBorder("拼图区"));
preview = new PicturePreview();
preview.setBorder(new TitledBorder("预览区"));
panel.add(canvas, BorderLayout.WEST);
panel.add(preview, BorderLayout.EAST);
this.add(panel, BorderLayout.CENTER);
}
// 添加组件
private void addComponent() {
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(1, 2));
JPanel leftPanel = new JPanel();
leftPanel.setBorder(new TitledBorder("按钮区"));
leftPanel.setBackground(Color.PINK);
addNumInfo = new JRadioButton("数字提示", false);
clearNumInfo = new JRadioButton("清除提示", true);
ButtonGroup buttonGroup = new ButtonGroup();
box = new JComboBox<>(items);
start = new JButton("Start");
buttonGroup.add(addNumInfo);
buttonGroup.add(clearNumInfo);
addNumInfo.setBackground(Color.PINK);
clearNumInfo.setBackground(Color.PINK);
start.setBackground(Color.PINK);
leftPanel.add(addNumInfo);
leftPanel.add(clearNumInfo);
leftPanel.add(new JLabel(" 选择图片:"));
leftPanel.add(box);
leftPanel.add(start);
panel.add(leftPanel, BorderLayout.WEST);
JPanel rightPanel = new JPanel();
rightPanel.setBorder(new TitledBorder("游戏状态"));
rightPanel.setBackground(Color.PINK);
rightPanel.setLayout(new GridLayout(1, 2));
name = new JTextField("图片名称:小女孩儿");
step = new JTextField("步数:0");
name.setEditable(false);
step.setEditable(false);
rightPanel.add(name, BorderLayout.WEST);
rightPanel.add(step, BorderLayout.EAST);
panel.add(rightPanel, BorderLayout.EAST);
this.add(panel, BorderLayout.NORTH);
}
// 界面初始化方法
private void init() {
this.setTitle("拼图游戏");
this.setSize(1000, 720);
this.setLocation(150, 10);
this.setResizable(false);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
在这一步中,我们完成了PictureMainFrame
类的基本框架,并实现了界面的初始化、添加组件和添加事件监听器等功能。下一步我们将继续完善其他类的代码。
下一步我们将完善PictureCanvas
类的代码,包括拼图小方格的创建、重新加载图片、开始游戏等功能。
步骤3:完善PictureCanvas
类
public class PictureCanvas extends JPanel implements MouseListener {
public static int pictureID = 1;
public static int stepNum = 0;
private final Cell[] cell;
private final Rectangle nullCell;
private boolean hasAddActionListener = false;
public PictureCanvas() {
this.setLayout(null);
cell = new Cell[12];
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 3; j++) {
ImageIcon icon = new ImageIcon("picture\\1_" + (i * 3 + j + 1) + ".gif");
cell[i * 3 + j] = new Cell(icon);
cell[i * 3 + j].setLocation(j * 150 + 20, i * 150 + 20);
this.add(cell[i * 3 + j]);
}
}
this.remove(cell[11]);
nullCell = new Rectangle(300 + 20, 450 + 20, 150, 150);
}
public void reLoadPictureAddNumber() {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 3; j++) {
ImageIcon icon = new ImageIcon("picture\\" + pictureID + "_" + (i * 3 + j + 1) + ".gif");
cell[i * 3 + j].setIcon(icon);
cell[i * 3 + j].setText("" + (i * 3 + j + 1));
cell[i * 3 + j].setVerticalTextPosition(this.getY() / 2);
cell[i * 3 + j].setHorizontalTextPosition(this.getX() / 2);
}
}
}
public void reLoadPictureClearNumber() {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 3; j++) {
ImageIcon icon = new ImageIcon("picture\\" + pictureID + "_" + (i * 3 + j + 1) + ".gif");
cell[i * 3 + j].setIcon(icon);
cell[i * 3 + j].setText("");
}
}
}
public void start() {
if (!hasAddActionListener) {
for (int i = 0; i < 11; i++) {
cell[i].addMouseListener(this);
}
hasAddActionListener = true;
}
while (cell[0].getBounds().x <= 170 && cell[0].getBounds().y <= 170) {
int nullX = nullCell.getBounds().x;
int nullY = nullCell.getBounds().y;
int direction = (int) (Math.random() * 4);
switch (direction) {
case 0:
nullX -= 150;
cellMove(nullX, nullY, "RIGHT");
break;
case 1:
nullX += 150;
cellMove(nullX, nullY, "LEFT");
break;
case 2:
nullY -= 150;
cellMove(nullX, nullY, "DOWN");
break;
case 3:
nullY += 150;
cellMove(nullX, nullY, "UP");
break;
}
}
}
private void cellMove(int nullX, int nullY, String direction) {
for (int i = 0; i < 11; i++) {
if (cell[i].getBounds().x == nullX && cell[i].getBounds().y == nullY) {
cell[i].move(direction);
nullCell.setLocation(nullX, nullY);
break;
}
}
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mousePressed(MouseEvent e) {
Cell button = (Cell) e.getSource();
int clickX = button.getBounds().x;
int clickY = button.getBounds().y;
int nullX = nullCell.getBounds().x;
int nullY = nullCell.getBounds().y;
if (clickX == nullX && clickY - nullY == 150) {
button.move("UP");
} else if (clickX == nullX && clickY - nullY == -150) {
button.move("DOWN");
} else if (clickX - nullX == 150 && clickY == nullY) {
button.move("LEFT");
} else if (clickX - null
X == -150 && clickY == nullY) {
button.move("RIGHT");
}
stepNum++;
PictureMainFrame.step.setText("步数:" + stepNum);
}
@Override
public void mouseReleased(MouseEvent e) {
boolean isSuccess = true;
for (int i = 0; i < 11; i++) {
if (!cell[i].isRightPosition()) {
isSuccess = false;
break;
}
}
if (isSuccess) {
JOptionPane.showMessageDialog(null, "拼图成功!用了" + stepNum + "步。");
reLoadPictureClearNumber();
PictureMainFrame.step.setText("步数:0");
stepNum = 0;
}
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
}
在这一步中,我们完成了PictureCanvas
类的基本框架,并实现了拼图小方格的创建、重新加载图片、开始游戏等功能。下一步我们将继续完善其他类的代码。
下一步我们将完善Cell
类和PicturePreview
类的代码,包括小方格的移动和图片预览功能。
步骤4:完善Cell
类和PicturePreview
类
public class Cell extends JButton {
public Cell(Icon icon) {
super(icon);
this.setSize(150, 150);
}
public void move(String direction) {
switch (direction) {
case "UP":
this.setLocation(this.getBounds().x, this.getBounds().y - 150);
break;
case "DOWN":
this.setLocation(this.getBounds().x, this.getBounds().y + 150);
break;
case "LEFT":
this.setLocation(this.getBounds().x - 150, this.getBounds().y);
break;
case "RIGHT":
this.setLocation(this.getBounds().x + 150, this.getBounds().y);
break;
default:
break;
}
}
public boolean isRightPosition() {
int x = this.getBounds().x;
int y = this.getBounds().y;
int index = (y - 20) / 150 * 3 + (x - 20) / 150;
return index == Integer.parseInt(this.getText()) - 1;
}
}
public class PicturePreview extends JPanel {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
String filename = "picture\\" + PictureCanvas.pictureID + ".jpg";
ImageIcon icon = new ImageIcon(filename);
Image image = icon.getImage();
g.drawImage(image, 20, 20, 450, 600, this);
}
}
在这一步中,我们完善了Cell
类和PicturePreview
类的代码,包括小方格的移动和图片预览功能。接下来我们将继续完善其他类的代码。
下一步,我们将完善PictureCanvas
类的代码,包括鼠标点击事件的处理和游戏完成的判断。
步骤5:完善PictureCanvas
类
public class PictureCanvas extends JPanel implements MouseListener {
public void mousePressed(MouseEvent e) {
Cell button = (Cell) e.getSource();
int clickX = button.getBounds().x;
int clickY = button.getBounds().y;
int nullX = nullCell.getBounds().x;
int nullY = nullCell.getBounds().y;
if (clickX == nullX && clickY - nullY == 150) {
button.move("UP");
} else if (clickX == nullX && clickY - nullY == -150) {
button.move("DOWN");
} else if (clickX - nullX == 150 && clickY == nullY) {
button.move("LEFT");
} else if (clickX - nullX == -150 && clickY == nullY) {
button.move("RIGHT");
} else {
return;
}
nullCell.setLocation(clickX, clickY);
this.repaint();
stepNum++;
PictureMainFrame.step.setText("步数:" + stepNum);
boolean isSuccess = true;
for (int i = 0; i < 11; i++) {
if (!cell[i].isRightPosition()) {
isSuccess = false;
break;
}
}
if (isSuccess) {
JOptionPane.showMessageDialog(null, "拼图成功!用了" + stepNum + "步。");
reLoadPictureClearNumber();
PictureMainFrame.step.setText("步数:0");
stepNum = 0;
}
}
public void mouseClicked(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}
在这一步中,我们完成了PictureCanvas
类的代码,包括鼠标点击事件的处理和游戏完成的判断。现在我们的拼图游戏已经基本完成,可以运行起来了。
步骤6:重命名+重构逻辑+优化洗牌逻辑(没有限制的重开+不会出现未解之谜)
public class PuzzleGameApp {
public static void main(String[] args) {
PuzzleGameFrame mainFrame = new PuzzleGameFrame();
mainFrame.setVisible(true);
}
}
import javax.swing.*;
import javax.swing.border.TitledBorder;
import java.awt.*;
public class PuzzleGameFrame extends JFrame {
public static JTextField stepCounter; // 步数计数器
private final String[] imageOptions = {"小女孩儿", "女明星"};
private JRadioButton showNumbersOption; // 显示数字选项
private JRadioButton hideNumbersOption; // 隐藏数字选项
private PuzzleArea puzzleArea; // 拼图区域
private ImagePreviewArea imagePreviewArea; // 图像预览区域
private JComboBox<String> imageSelector; // 图片选择下拉框
private JTextField imageNameField; // 图片名称字段
private JButton startButton; // 开始按钮
public PuzzleGameFrame() {
initializeUI();
addComponents();
setupInitialImages();
setupListeners();
}
private void setupListeners() {
showNumbersOption.addActionListener(e -> puzzleArea.loadImageWithNumbers());
hideNumbersOption.addActionListener(e -> puzzleArea.loadImageWithoutNumbers());
imageSelector.addItemListener(e -> {
int selectedIndex = imageSelector.getSelectedIndex();
PuzzleArea.imageId = selectedIndex + 1;
imagePreviewArea.repaint();
puzzleArea.loadImageWithoutNumbers();
imageNameField.setText("图片名称:" + imageSelector.getSelectedItem());
int steps = PuzzleArea.steps = 0;
stepCounter.setText("步数:" + steps);
hideNumbersOption.setSelected(true);
});
startButton.addActionListener(e -> {
PuzzleArea.steps = 0;
stepCounter.setText("步数:" + PuzzleArea.steps);
puzzleArea.startPuzzle();
});
}
private void setupInitialImages() {
JPanel panel = new JPanel(new GridLayout(1, 2));
puzzleArea = new PuzzleArea();
puzzleArea.setBorder(new TitledBorder("拼图区域"));
imagePreviewArea = new ImagePreviewArea();
imagePreviewArea.setBorder(new TitledBorder("预览区域"));
panel.add(puzzleArea, BorderLayout.WEST);
panel.add(imagePreviewArea, BorderLayout.EAST);
this.add(panel, BorderLayout.CENTER);
}
private void addComponents() {
JPanel mainPanel = new JPanel(new GridLayout(1, 2));
JPanel leftPanel = new JPanel();
leftPanel.setBorder(new TitledBorder("控制区"));
leftPanel.setBackground(Color.PINK);
showNumbersOption = new JRadioButton("显示数字", false);
hideNumbersOption = new JRadioButton("隐藏数字", true);
ButtonGroup optionGroup = new ButtonGroup();
imageSelector = new JComboBox<>(imageOptions);
startButton = new JButton("开始");
optionGroup.add(showNumbersOption);
optionGroup.add(hideNumbersOption);
showNumbersOption.setBackground(Color.PINK);
hideNumbersOption.setBackground(Color.PINK);
startButton.setBackground(Color.PINK);
leftPanel.add(showNumbersOption);
leftPanel.add(hideNumbersOption);
leftPanel.add(new JLabel("选择图片:"));
leftPanel.add(imageSelector);
leftPanel.add(startButton);
mainPanel.add(leftPanel, BorderLayout.WEST);
JPanel rightPanel = new JPanel();
rightPanel.setBorder(new TitledBorder("游戏状态"));
rightPanel.setBackground(Color.PINK);
rightPanel.setLayout(new GridLayout(1, 2));
imageNameField = new JTextField("图片名称:小女孩儿");
stepCounter = new JTextField("步数:0");
imageNameField.setEditable(false);
stepCounter.setEditable(false);
rightPanel.add(imageNameField, BorderLayout.WEST);
rightPanel.add(stepCounter, BorderLayout.EAST);
mainPanel.add(rightPanel, BorderLayout.EAST);
this.add(mainPanel, BorderLayout.NORTH);
}
private void initializeUI() {
this.setTitle("拼图游戏");
this.setSize(1000, 720);
this.setLocation(150, 10);
this.setResizable(false);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
public class PuzzleArea extends JPanel implements MouseListener {
public static int imageId = 1;
public static int steps = 0;
private final PuzzlePiece[] puzzlePieces;
private final Rectangle emptySpot;
private boolean isListenerAdded = false;
public PuzzleArea() {
this.setLayout(null);
puzzlePieces = new PuzzlePiece[12];
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 3; j++) {
ImageIcon icon = new ImageIcon("picture\\1_" + (i * 3 + j + 1) + ".gif");
puzzlePieces[i * 3 + j] = new PuzzlePiece(icon);
puzzlePieces[i * 3 + j].setLocation(j * 150 + 20, i * 150 + 20);
this.add(puzzlePieces[i * 3 + j]);
}
}
this.remove(puzzlePieces[11]);
emptySpot = new Rectangle(300 + 20, 450 + 20, 150, 150);
}
public void loadImageWithNumbers() {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 3; j++) {
ImageIcon icon = new ImageIcon("picture\\" + imageId + "_" + (i * 3 + j + 1) + ".gif");
puzzlePieces[i * 3 + j].setIcon(icon);
puzzlePieces[i * 3 + j].setText("" + (i * 3 + j + 1));
puzzlePieces[i * 3 + j].setVerticalTextPosition(this.getY() / 2);
puzzlePieces[i * 3 + j].setHorizontalTextPosition(this.getX() / 2);
}
}
}
public void loadImageWithoutNumbers() {
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 3; j++) {
ImageIcon icon = new ImageIcon("picture\\" + imageId + "_" + (i * 3 + j + 1) + ".gif");
puzzlePieces[i * 3 + j].setIcon(icon);
puzzlePieces[i * 3 + j].setText("");
}
}
}
public void startPuzzle() {
if (!isListenerAdded) {
for (int i = 0; i < 11; i++) {
puzzlePieces[i].addMouseListener(this);
}
isListenerAdded = true;
}
shufflePieces();
}
private void shufflePieces() {
int shuffleCount = 100; // 定义一个合适的打乱次数
for (int i = 0; i < shuffleCount; i++) {
ArrayList<String> possibleMoves = new ArrayList<>();
int nullX = emptySpot.x;
int nullY = emptySpot.y;
// 检查四个可能的移动方向并确认是否可行
if (nullX > 20) { // 检查左边是否有块可以移动到空位
possibleMoves.add("LEFT");
}
if (nullX < 300) { // 检查右边是否有块可以移动到空位
possibleMoves.add("RIGHT");
}
if (nullY > 20) { // 检查上面是否有块可以移动到空位
possibleMoves.add("UP");
}
if (nullY < 300) { // 检查下面是否有块可以移动到空位
possibleMoves.add("DOWN");
}
// 从可能的移动中随机选择一个执行
String move = possibleMoves.get((int) (Math.random() * possibleMoves.size()));
switch (move) {
case "LEFT":
movePiece(nullX - 150, nullY, "RIGHT");
break;
case "RIGHT":
movePiece(nullX + 150, nullY, "LEFT");
break;
case "UP":
movePiece(nullX, nullY - 150, "DOWN");
break;
case "DOWN":
movePiece(nullX, nullY + 150, "UP");
break;
}
}
}
private void movePiece(int nullX, int nullY, String direction) {
for (int i = 0; i < 11; i++) {
if (puzzlePieces[i].getBounds().x == nullX && puzzlePieces[i].getBounds().y == nullY) {
puzzlePieces[i].move(direction);
emptySpot.setLocation(nullX, nullY);
break;
}
}
}
@Override
public void mousePressed(MouseEvent e) {
PuzzlePiece clickedPiece = (PuzzlePiece) e.getSource();
int clickX = clickedPiece.getBounds().x;
int clickY = clickedPiece.getBounds().y;
int nullX = emptySpot.getBounds().x;
int nullY = emptySpot.getBounds().y;
if (clickX == nullX && clickY - nullY == 150) {
clickedPiece.move("UP");
} else if (clickX == nullX && clickY - nullY == -150) {
clickedPiece.move("DOWN");
} else if (clickX - nullX == 150 && clickY == nullY) {
clickedPiece.move("LEFT");
} else if (clickX - nullX == -150 && clickY == nullY) {
clickedPiece.move("RIGHT");
} else {
return;
}
emptySpot.setLocation(clickX, clickY);
this.repaint();
steps++;
PuzzleGameFrame.stepCounter.setText("步数:" + steps);
checkPuzzleCompletion();
}
private void checkPuzzleCompletion() {
boolean isComplete = true;
for (int i = 0; i < 11; i++) {
if (!puzzlePieces[i].isCorrectPosition()) {
isComplete = false;
break;
}
}
if (isComplete) {
JOptionPane.showMessageDialog(null, "拼图成功!用了" + steps + "步。");
loadImageWithoutNumbers();
PuzzleGameFrame.stepCounter.setText("步数:0");
steps = 0;
}
}
@Override
public void mouseClicked(MouseEvent e) {
}
@Override
public void mouseReleased(MouseEvent e) {
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
}
import javax.swing.*;
public class PuzzlePiece extends JButton {
public PuzzlePiece(Icon icon) {
super(icon);
this.setSize(150, 150);
}
public void move(String direction) {
switch (direction) {
case "UP":
this.setLocation(this.getBounds().x, this.getBounds().y - 150);
break;
case "DOWN":
this.setLocation(this.getBounds().x, this.getBounds().y + 150);
break;
case "LEFT":
this.setLocation(this.getBounds().x - 150, this.getBounds().y);
break;
case "RIGHT":
this.setLocation(this.getBounds().x + 150, this.getBounds().y);
break;
default:
break;
}
}
public boolean isCorrectPosition() {
int x = this.getBounds().x;
int y = this.getBounds().y;
int index = (y - 20) / 150 * 3 + (x - 20) / 150;
return index == Integer.parseInt(this.getText()) - 1;
}
}
import javax.swing.*;
import java.awt.*;
public class ImagePreviewArea extends JPanel {
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
String filename = "picture\\" + PuzzleArea.imageId + ".jpg";
ImageIcon icon = new ImageIcon(filename);
Image image = icon.getImage();
g.drawImage(image, 20, 20, 450, 600, this);
}
}
细节
ButtonGroup 可以保证一组按钮只有一个被选中
addActionListener() 必须在 addComponent() 和 addPreviewImage() 之后调用, 因为 addComponent() 和addPreviewImage() 中完成了对 成员变量 的初始化
在Java Swing中,this.setLayout(null); 这行代码用于设置容器(在这个例子中是 PuzzleArea 类,一个继承自 JPanel 的类)的布局管理器为 null。这意味着该容器不使用任何自动布局管理器来控制其子组件的位置和大小。
当你将布局管理器设置为 null 时,你需要手动指定容器中每个组件的大小和位置。这通常是通过调用组件的 setBounds(x, y, width, height) 或 setLocation(x, y) 和 setSize(width, height) 方法来实现的。这种方式给予了开发者完全的控制权,可以精确地放置和调整组件,但同时也需要更多的工作来确保组件在不同环境(如不同的屏幕分辨率和字体设置)下表现良好。
在 PuzzleArea 类中,使用 null 布局是为了能精确地控制拼图块的位置,因为拼图游戏需要拼图块能够精确地放置在特定的位置。例如,每个拼图块通过 setLocation 方法被放置在特定的坐标上,这些坐标对于游戏的运行至关重要,因为它们决定了拼图块能否正确地拼合。