一、拼图小游戏
1. Java Swing 图形界面 中处理「鼠标点击事件」的逻辑
java
@Override
public void mouseClicked(MouseEvent e) {
if (e.getSource() == submit) { // 不太理解
if (username.getText().length() == 0 || password.getText().length() == 0 || rePassword.getText().length() == 0) {
showDialog("用户名和密码不能为空");
return; // 不太理解
}
if (!password.getText().equals(rePassword.getText())) {
showDialog("两次密码输入不一致");
return;
}
if (!username.getText().matches("[a-zA-Z0-9]{4,16}")) {
showDialog("用户名不符合规则");
return;
}
if (!password.getText().matches("\\S*(?=\\S{6,})(?=\\S*\\d)(?=\\S*[a-z])\\S*")) {
showDialog("密码不符合规则,至少包含一个小写字母,一个数字,长度至少6位");
return;
}
if (containUsername(username.getText())) {
showDialog("用户名已经存在,请重新输入");
return;
}
allUsers.add(new User(username.getText(),password.getText()));
FileUtil.writeLines(allUsers,"D:\\张锐彬\\Java\\01-登录注册学生练习代码\\puzzlegame\\puzzlegame\\userinfo.txt","UTF-8");
showDialog("注册成功");
this.setVisible(false); // 不太理解
new LoginJFrame();
} else if (e.getSource() == reset) {
username.setText("");
password.setText("");
rePassword.setText("");
}
}
关键逻辑 1:if (e.getSource() == submit)
java
if (e.getSource() == submit) { ... } else if (e.getSource() == reset) { ... }
e:是MouseEvent类型的对象,代表「鼠标点击事件」的所有信息(比如点击的位置、点击的组件等)。e.getSource():获取「触发这次鼠标点击事件的组件」(比如用户点击了「提交」按钮,就返回这个按钮对象;点击「重置」按钮,就返回重置按钮对象)。submit和reset:是你在界面中定义的两个按钮对象(比如JButton submit = new JButton("提交");)。- 整个判断的意思:判断用户点击的是「提交」按钮还是「重置」按钮,分别执行不同逻辑。
关键逻辑 2: 注册校验:非空判断 + return
java
if (username.getText().length() == 0 || password.getText().length() == 0 || rePassword.getText().length() == 0) {
showDialog("用户名和密码不能为空");
return; // 不太理解
}
username、password、rePassword:是界面中的输入框组件(比如JTextField username = new JTextField();)。getText():获取输入框中用户输入的文本。length() == 0:判断输入框是否为空(文本长度为 0)。showDialog(...):自定义方法(推测是弹出一个提示框,显示错误信息)。- 关键:
return的作用 :一旦发现「用户名 / 密码为空」,弹出提示后,立即终止当前方法的执行 (后面的密码一致性校验、格式校验等逻辑都不会再执行)。为什么要加return?比如用户没输入内容就点提交,此时已经不符合注册条件,再执行后面的逻辑毫无意义,还可能导致错误(比如空字符串的格式校验失败),所以直接返回,节省资源。
关键逻辑 3:注册成功后的逻辑
java
// 1. 将新用户添加到内存中的用户列表(allUsers 是自定义的集合,比如 List<User>)
allUsers.add(new User(username.getText(), password.getText()));
// 2. 将用户列表写入本地文件(FileUtil 是自定义工具类,用于文件操作)
FileUtil.writeLines(allUsers, "D:\\...\\userinfo.txt", "UTF-8");
// 3. 弹出注册成功提示
showDialog("注册成功");
// 4. 隐藏当前注册窗口(this 指代当前的注册界面,比如 RegisterJFrame)
this.setVisible(false); // 不太理解
// 5. 创建并显示登录窗口(LoginJFrame 是登录界面的类)
new LoginJFrame();
- 关键:
this.setVisible(false)的作用 :this指代「当前的注册窗口对象」(因为这个方法所在的类是注册窗口类,比如RegisterJFrame)。setVisible(false)是 Swing 组件的方法,意思是「让当前窗口隐藏」(不再显示在屏幕上)。为什么要隐藏?注册成功后,用户需要跳转到「登录界面」,所以先把注册窗口关掉(隐藏),再创建登录窗口并显示。
关键逻辑 5:重置按钮的逻辑
java
else if (e.getSource() == reset) {
username.setText(""); // 清空用户名输入框
password.setText(""); // 清空密码输入框
rePassword.setText(""); // 清空重复密码输入框
}
- 当用户点击「重置」按钮时,调用输入框的
setText("")方法,将输入框中的文本设为空字符串,实现「清空输入」的功能。
2. 游戏存档 和读档的功能
java
// 存档功能
else if(obj == saveItem0 || obj == saveItem1 || obj == saveItem2 || obj == saveItem3 ||obj == saveItem4){
System.out.println("存档");
JMenuItem item = (JMenuItem) obj; // 不太理解
String str = item.getText();
int index = str.charAt(2) - '0'; // 不太理解
try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("D:\\张锐彬\\Java\\02-游戏存档和读档学生练习代码\\puzzlegame\\puzzlegame\\save\\save" + index + ".txt"));
GameInfo gi = new GameInfo(data,x,y,path,step); // 不太理解
IoUtil.writeObj(oos,true,gi); // 不太理解
} catch (IOException ex) {
throw new RuntimeException(ex);
}
item.setText("存档" + index + "(" + step + "步)"); // 不太理解
loadJMenu.getItem(index).setText("存档" + index + "(" + step + "步)");
}
// 读档功能
else if(obj == loadItem0 || obj == loadItem1 || obj == loadItem2 || obj == loadItem3 ||obj == loadItem4){
JMenuItem item = (JMenuItem) obj;
String str = item.getText();
int index = str.charAt(2) - '0';
GameInfo gi = null;
try {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("D:\\张锐彬\\Java\\02-游戏存档和读档学生练习代码\\puzzlegame\\puzzlegame\\save\\save" + index + ".txt"));
gi = (GameInfo)ois.readObject(); // 不太理解
ois.close();
} catch (IOException ex) {
throw new RuntimeException(ex);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(ex);
}
data = gi.getData(); // 不太理解
path = gi.getPath();
step = gi.getStep();
x = gi.getX();
y = gi.getY();
initImage();
}
关键逻辑 1:
(1) 存档功能 (else if 块)
关键逻辑 1:else if(obj == saveItem0 || obj == saveItem1 || ...)
- 这是一个条件判断,用来识别用户点击的是哪个菜单项。
obj是触发事件的组件对象,这里就是被点击的菜单项。- 整个判断的意思是:如果用户点击的是
saveItem0到saveItem4这五个菜单项中的任意一个,就执行下面的存档逻辑。
关键逻辑 2:JMenuItem item = (JMenuItem) obj;
- 核心目的 :我们需要调用这个菜单项的特定方法(如
getText()和setText()),但obj的类型是Object,编译器不知道它具体是JMenuItem类型。 (JMenuItem):这是强制类型转换 。我们明确告诉编译器:"我知道obj实际上是一个JMenuItem对象,请把它当作JMenuItem来处理"。
关键逻辑 3:int index = str.charAt(2) - '0';
- 这是一个从字符串中提取数字的常用技巧。
str.charAt(2):获取字符串str中索引为 2 的字符。对于"存档0(空)",这个字符就是'0'。'0':在 Java 中,字符用单引号表示。这里的关键是,字符'0'到'9'在计算机内部是按顺序存储的。str.charAt(2) - '0':这是一个数学运算。计算机会使用字符的 ASCII 码值来计算。例如,'3' - '0'的结果是整数3。- 最终目的 :通过这种方式,我们从菜单项文本中提取出了存档的编号(0 到 4),并将其存储在整数变量
index中。
关键逻辑 4:ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("...save" + index + ".txt"));
- 这行代码创建了一个用于对象序列化的输出流。
new FileOutputStream("..."):创建一个文件输出流,指定了文件保存的路径和文件名。"save" + index + ".txt"会动态生成文件名,如save0.txt,save1.txt等。new ObjectOutputStream(...):将文件输出流包装成对象输出流。ObjectOutputStream可以将 Java 对象(如我们的游戏状态)转换成字节流,以便写入文件。
关键逻辑 5:GameInfo gi = new GameInfo(data, x, y, path, step);
GameInfo是一个你自定义的类,它的作用就像一个数据容器 或快照。- 这行代码创建了一个
GameInfo对象,并将当前游戏的所有关键状态(data数组、空白块位置x,y、图片路径path、步数step)作为参数传递给它的构造函数。 - 这样,
gi对象就完整地记录了游戏在这一刻的状态。
关键逻辑 6:item.setText("存档" + index + "(" + step + "步)");
- 存档成功后,更新当前点击的存档菜单项的显示文本。
- 例如,将 "存档 0 (空)" 更新为 "存档 0 (15 步)"。
- 目的:给用户一个清晰的视觉反馈,让用户知道该存档位已经使用,并且记录了游戏进行到第几步。
(2) 读档功能
关键逻辑 1:ObjectInputStream ois = new ObjectInputStream(new FileInputStream("...save" + index + ".txt"));
- 这行代码创建了一个用于对象反序列化的输入流。
new FileInputStream("..."):创建一个文件输入流,用于读取指定的存档文件。new ObjectInputStream(...):将文件输入流包装成对象输入流。ObjectInputStream可以从字节流中读取数据,并将其还原成一个 Java 对象。
关键逻辑 2:gi = (GameInfo) ois.readObject();
ois.readObject():从输入流中读取一个对象。这个方法返回的是一个Object类型的引用。(GameInfo):因为我们知道文件里存储的是一个GameInfo对象,所以需要进行强制类型转换,将读取到的Object转换成GameInfo类型。