项目需求
在拼图小游戏基础版的基础上,完成下列要求:
一、实现更换拼图图片功能
1,给美女,动物,运动菜单按钮添加单击事件(动作监听)
2,当我们点击了美女之后,就会从13组美女图片中随机选择一组。
3,当我们点击了动物之后,就会从8组动物图片中随机选择一组。
4,当我们点击了运动之后,就会从10组运动图片中随机选择一组。
5,细节1:更换完毕之后,游戏界面中需要加载所有的小图片并且打乱顺序
6,细节2:更换完毕之后,查看全图的功能显示的是更换后图片的全图
二、完成登录界面
1,界面搭建。
2,用静态代码块准备一些初始的用户信息
3,点击登录按钮之后的逻辑:
按下登录不松,切换登录按钮的深色背景图片
松开登录按钮,逻辑较为复杂
获取用户输入的用户名,密码,验证码。
比较验证码 提示正确、错误
判断用户名和密码是否为空,只要有一个为空就不行
细节:如果用户没有输入用户名和密码,在代码中获取的不是null,而是长度为0的字符串
为空时,提示用户名和密码为空
用户名,密码比较正确,跳转游戏界面
用户名,密码比较错误,提示错误
4,提示全部以弹窗的形式提出
5,点击注册按钮之后的逻辑
暂时不需要写逻辑,后面学习完IO的时候再补
6,点击验证码字符串后
更换一个新的验证码(写一个工具类提供验证码)
分析
一、实现更换拼图图片功能
在这个界面中,我们需要哪些技术点:
整个的菜单就是JMenuBar
功能,关于我们:JMenu
更换图片:JMenu
重新游戏,重新登录,关闭游戏,美女,动物,运动:JMenuItem
特点:如果在菜单中,还需要嵌套二级的菜单,那么可以用JMenu完成。JMenu里面是可以再次添加其他的JMenu的。
写代码的时候如何实现:
第一步:创建JMenuBar对象
第二步:创建三个JMenu对象(功能,关于我们,更换图片)
第三步:创建六个JMenuItem对象(重新游戏,重新登录,关闭游戏,美女,动物,运动)
第四步:把美女,动物,运动放到更换图片当中
第五步:把更换图片,重新游戏,重新登录,关闭游戏放到功能当中
第六步:把功能,关于我们放到JMenuBar
第七步:把JMenuBar放到整个界面当中
二、完成登录界面
所需要的技术点
1.用户名文字其实是一张图片,还是用JLabel去管理ImageIcon
用户名输入框:JTextField(明文显示的输入框)
2.密码文字其实是一张图片,还是用JLabel去管理ImageIcon
密码输入框:JPasswordField(密文显示的输入框)
3.验证码文字其实是一张图片,还是用JLabel去管理ImageIcon
验证码输入框:JTextField(明文显示的输入框)
4.验证码wyS7i:用JLabel去管理文字,需要自己写一个生成验证码的工具类。
5.登录与注册两个都是按钮,当点击按钮不松的时候,按钮变灰,其实就是换一个深色的背景图。
代码实现
App类
public class App {
public static void main(String[] args) {
//创建登录界面,在登录界面中成功登录后创建游戏界面
new LoginJFrame();
}
}
登录界面类
import javax.swing.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
public class LoginJFrame extends JFrame implements MouseListener {
//不止一个方法使用的变量定义在成员变量位置
JButton login;
JButton register;
JTextField username;
JTextField password;
JTextField code;
String codeStr;
JLabel showCode;
JDialog jDialog;
static ArrayList<User> list = new ArrayList();
//用静态代码块准备一些初始的用户信息,目前不写注册功能,用初始信息登录
static {
User u1 = new User("zhangsan", "123456");
User u2 = new User("lisi", "12345678");
list.add(u1);
list.add(u2);
}
//空参构造初始化登录
public LoginJFrame() {
//调用方法初始化登录界面
initFrame();
//调用方法加载登录界面图式
loadImages();
//添加事件监听,鼠标监听事件MouseListener
login.addMouseListener(this);
register.addMouseListener(this);
showCode.addMouseListener(this);
//设置界面可视,建议放在最后
this.setVisible(true);
}
//定义方法加载登录界面图式
private void loadImages() {
//1. 添加用户名文字
JLabel usernameText = new JLabel(new ImageIcon("puzzlegame\\image\\login\\用户名.png"));
usernameText.setBounds(116, 135, 47, 17);
this.getContentPane().add(usernameText);
//2.添加用户名输入框
username = new JTextField();
username.setBounds(195, 134, 200, 30);
this.getContentPane().add(username);
//3.添加密码文字
JLabel passwordText = new JLabel(new ImageIcon("puzzlegame\\image\\login\\密码.png"));
passwordText.setBounds(130, 195, 32, 16);
this.getContentPane().add(passwordText);
//4.密码输入框
password = new JTextField();
password.setBounds(195, 195, 200, 30);
this.getContentPane().add(password);
//验证码提示
JLabel codeText = new JLabel(new ImageIcon("puzzlegame\\image\\login\\验证码.png"));
codeText.setBounds(133, 256, 50, 30);
this.getContentPane().add(codeText);
//验证码的输入框
code = new JTextField();
code.setBounds(195, 256, 100, 30);
this.getContentPane().add(code);
//调用工具类生成验证码
codeStr = CodeUtil.getCode();
//显示生成的验证码
showCode = new JLabel();
//设置内容
showCode.setText(codeStr);
//位置和宽高
showCode.setBounds(300, 256, 50, 30);
//添加到界面
this.getContentPane().add(showCode);
//5.添加登录按钮
login = new JButton();
login.setBounds(123, 310, 128, 47);
//设置按钮背景图
login.setIcon(new ImageIcon("puzzlegame\\image\\login\\登录按钮.png"));
//去除按钮的默认边框
login.setBorderPainted(false);
//去除按钮的默认背景
login.setContentAreaFilled(false);
this.getContentPane().add(login);
//6.添加注册按钮
register = new JButton();
register.setBounds(256, 310, 128, 47);
//设置按钮背景图
register.setIcon(new ImageIcon("puzzlegame\\image\\login\\注册按钮.png"));
//去除按钮的默认边框
register.setBorderPainted(false);
//去除按钮的默认背景
register.setContentAreaFilled(false);
this.getContentPane().add(register);
//7.添加界面背景图片
JLabel background = new JLabel(new ImageIcon("puzzlegame\\image\\login\\background.png"));
background.setBounds(0, 0, 470, 390);
this.getContentPane().add(background);
}
//定义方法初始化登录界面
private void initFrame() {
this.setSize(488, 430);
this.setTitle("拼图游戏登录");
this.setAlwaysOnTop(true);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(3);
this.setLayout(null);
}
//定义方法判断用户名密码是否正确
private boolean checkUser(ArrayList<User> list, User u) {
int index = checkUserName(list, u);
if (index == -1) {
//System.out.println("用户名和密码错误");
return false;
}
//到这步,证明用户名存在,判断对应的密码是否相同
if (list.get(index).getPassWord().equals(u.getPassWord())) {
//相同返回true
return true;
}
//不同返回false
return false;
}
//定义方法查询用户名是否在集合中存在,存在返回索引,不存在返回-1
private int checkUserName(ArrayList<User> list, User u) {
for (int i = 0; i < list.size(); i++) {
if (list.get(i).getUserName().equals(u.getUserName())) {
return i;
}
}
return -1;
}
//定义方法展示弹窗
private void showDialog(String s) {
jDialog = new JDialog();
//设置弹框的宽和高:100,100
jDialog.setSize(100, 100);
//设置弹框居中
jDialog.setLocationRelativeTo(null);
//设置弹框置顶
jDialog.setAlwaysOnTop(true);
//设置关闭后进行其他操作
jDialog.setModal(true);
//创建一个JLabel去编写文本内容
JLabel textJlabel = new JLabel(s);
textJlabel.setBounds(20, 50, 120, 60);
//把文本JLabel添加到弹框当中
jDialog.getContentPane().add(textJlabel);
//把弹框展示出来
jDialog.setVisible(true);
}
//鼠标监听事件MouseListener
//鼠标单击调用该方法
@Override
public void mouseClicked(MouseEvent e) {
Object source = e.getSource();
if (source == login) {
//如果是登录按钮,获取输入框中的用户名,密码,验证码
String myUserName = username.getText();
String myPassWord = password.getText();
String myCode = code.getText();
//判断是否为空,如果为空,提示:用户名或密码为空
if (myUserName.equals("") || myPassWord.equals("")) {
//展示弹框:用户名或密码为空
//调用方法展示弹框,参数为需要展示的文字
showDialog("用户名或密码为空");
//换一个验证码
codeStr = CodeUtil.getCode();
showCode.setText(codeStr);
return;
}
//判断验证码是否正确
if (!(myCode.equalsIgnoreCase(codeStr))) {
//展示弹框:验证码错误
showDialog("验证码错误");
System.out.println("验证码错误");
//换一个验证码
codeStr = CodeUtil.getCode();
showCode.setText(codeStr);
return;
}
//判断用户名和密码是否为正确,如果正确隐藏登录界面,进入游戏界面。
//调用方法判断
User myUser = new User(myUserName, myPassWord);
boolean flag = checkUser(list, myUser);
if (!flag) {
//展示弹框:用户名或密码错误
showDialog("用户名或密码错误");
System.out.println("用户名或密码错误");
//换一个验证码
codeStr = CodeUtil.getCode();
showCode.setText(codeStr);
return;
}
//到这步,证明用户名和密码正确
//隐藏登录界面,进入游戏界面
this.setVisible(false);
new GameJFrame();
} else if (source == showCode) {
//重新生成验证码
codeStr = CodeUtil.getCode();
showCode.setText(codeStr);
}
}
//鼠标按下调用该方法
@Override
public void mousePressed(MouseEvent e) {
Object source = e.getSource();
if (source == login) {
//登录按钮
//按下不松的时候利用setIcon方法,修改登录按钮的背景色
login.setIcon(new ImageIcon("PuzzleGame\\image\\login\\登录按下.png"));
} else if (source == register) {
//注册按钮
//按下不松的时候利用setIcon方法,修改注册按钮的背景色
register.setIcon(new ImageIcon("PuzzleGame\\image\\login\\注册按下.png"));
}
}
//鼠标释放调用该方法
@Override
public void mouseReleased(MouseEvent e) {
Object source = e.getSource();
//登录按钮
//释放的时候利用setIcon方法,恢复登录按钮的背景色
if (source == login) {
login.setIcon(new ImageIcon("PuzzleGame\\image\\login\\登录按钮.png"));
} else if (source == register) {
//注册按钮
//释放的时候利用setIcon方法,恢复注册按钮的背景色
register.setIcon(new ImageIcon("PuzzleGame\\image\\login\\注册按钮.png"));
}
}
//鼠标划入调用该方法
@Override
public void mouseEntered(MouseEvent e) {
}
//鼠标划出调用该方法
@Override
public void mouseExited(MouseEvent e) {
}
}
游戏界面类
import javax.swing.*;
import javax.swing.border.BevelBorder;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;
//游戏界面类继承界面类,实现键盘监听和动作监听
public class GameJFrame extends JFrame implements KeyListener, ActionListener {
//多个方法需要用到的变量,记录在成员变量的位置
//定义二维数组记录打乱后的0~15,每一个数字对应一张拼图
int data[][] = new int[4][4];
//定义胜利时的数据数组
int[][] winArr = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12},
{13, 14, 15, 0}
};
//定义坐标记录0的位置
int x = 0;
int y = 0;
//定义计数器记录移动步数
int stepCount = 0;
//创建菜单选项里下面的条目
JMenuItem restartItem = new JMenuItem("重新开始");
JMenuItem reLoginItem = new JMenuItem("重新登录");
JMenuItem exitItem = new JMenuItem("退出游戏");
JMenuItem animalItem = new JMenuItem("动物");
JMenuItem girllItem = new JMenuItem("美女");
JMenuItem sportItem = new JMenuItem("运动");
JMenuItem AccountItem = new JMenuItem("公众号");
//定义图片路径,方便后面修改
String path = "PuzzleGame\\image\\animal\\animal1\\";
Random r = new Random();
//空参构造初始化游戏
public GameJFrame() {
//调用方法初始化界面
initFrame();
//给界面添加事件监听,键盘监听KeyListener
this.addKeyListener(this);
//调用方法添加菜单
initMenu();
//给菜单里的条目添加事件监听,动作监听ActionListener
restartItem.addActionListener(this);
reLoginItem.addActionListener(this);
exitItem.addActionListener(this);
AccountItem.addActionListener(this);
animalItem.addActionListener(this);
girllItem.addActionListener(this);
sportItem.addActionListener(this);
//调用方法初始化数据
initData();
//调用方法根据初始化后的数据加载图片
loadImages();
//设置界面可视,建议放在最后
this.setVisible(true);
}
//定义方法判断游戏是否胜利
private boolean victoty() {
for (int i = 0; i < data.length; i++) {
for (int j = 0; j < data[i].length; j++) {
if (data[i][j] != winArr[i][j]) {
//data中的数据有一个与胜利数组中的数据不同,就说明没有胜利
return false;
}
}
}
return true;
}
//定义方法根据初始化后的数据加载图片
private void loadImages() {
//清空所有图片
this.getContentPane().removeAll();
//如果胜利,加载胜利图标
if (victoty()) {
JLabel vicJLabel = new JLabel(new ImageIcon("PuzzleGame\\image\\win.png"));
vicJLabel.setBounds(203, 283, 197, 73);
this.getContentPane().add(vicJLabel);
}
//加载计数器
JLabel countJLabel = new JLabel("步数:" + stepCount);
countJLabel.setBounds(50, 30, 100, 20);
this.getContentPane().add(countJLabel);
//利用循环加载拼图图片
for (int i = 0; i < data.length; i++) {
for (int j = 0; j < data[i].length; j++) {
//获得data数组里的数据
int number = data[i][j];
//根据数据创建图片对象
ImageIcon icon = new ImageIcon(path + number + ".jpg");
//创建管理容器,将图片交给管理容器
JLabel jLabel = new JLabel(icon);
//设置管理容器位置,大小
jLabel.setBounds(105 * j + 83, 105 * i + 134, 105, 105);
//设置边框
jLabel.setBorder(new BevelBorder(0));
//将管理容器添加到界面中
this.getContentPane().add(jLabel);
}
}
//添加背景图片
JLabel bgJLabel = new JLabel(new ImageIcon("PuzzleGame\\image\\background.png"));
bgJLabel.setBounds(40, 40, 508, 560);
this.getContentPane().add(bgJLabel);
//刷新一下
this.getContentPane().repaint();
}
//定义方法初始化数据
private 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;
}
//将打乱后的数据记录在二维数组data中
for (int i = 0; i < tempArr.length; i++) {
//如果是0,记录0的位置
if (tempArr[i] == 0) {
x = i / 4;
y = i % 4;
}
data[i / 4][i % 4] = tempArr[i];
}
}
//定义方法添加菜单
private void initMenu() {
//创建菜单
JMenuBar jMenuBar = new JMenuBar();
//创建菜单的选项
JMenu functionJmenu = new JMenu("功能");
JMenu aboutJmenu = new JMenu("关于我们");
//嵌套二级菜单,JMenu里面是可以再次添加其他的JMenu
JMenu updateJmenu = new JMenu("更换图片");
updateJmenu.add(animalItem);
updateJmenu.add(girllItem);
updateJmenu.add(sportItem);
functionJmenu.add(updateJmenu);
//将条目添加到选项中
functionJmenu.add(restartItem);
functionJmenu.add(reLoginItem);
functionJmenu.add(exitItem);
aboutJmenu.add(AccountItem);
//将选项添加到菜单中
jMenuBar.add(functionJmenu);
jMenuBar.add(aboutJmenu);
//将菜单添加到界面中
this.setJMenuBar(jMenuBar);
}
//定义方法初始化界面
private void initFrame() {
//设置大小
this.setSize(603, 680);
//设置标题
this.setTitle("拼图游戏");
//设置居中
this.setLocationRelativeTo(null);
//设置置顶
this.setAlwaysOnTop(true);
//设置关闭模式
this.setDefaultCloseOperation(3);
//设置解除默认居中放置,只有解除了,才能根据xy轴的方式添加主件
this.setLayout(null);
}
//动作监听ActionListener,鼠标单击或按空格调用该方法
@Override
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if (source == restartItem) {
//重新开始
//重新初始化数据
initData();
//计数器归0
stepCount = 0;
//重新根据初始化后的数据加载图片
loadImages();
} else if (source == reLoginItem) {
//重新登录
//关闭本类(游戏类)
this.setVisible(false);
//创建登录界面
new LoginJFrame();
} else if (source == exitItem) {
//退出游戏
System.exit(0);
} else if (source == AccountItem) {
//显示公众号
//创建弹窗
JDialog accJDialog = new JDialog();
//加载弹窗中的图片
JLabel aboutJLabel = new JLabel(new ImageIcon("PuzzleGame\\image\\about.png"));
aboutJLabel.setBounds(0, 0, 258, 258);
//将图片添加到弹窗中
accJDialog.getContentPane().add(aboutJLabel);
//设置弹窗的大小
accJDialog.setSize(344, 344);
//设置弹窗置顶
accJDialog.setAlwaysOnTop(true);
//设置弹窗居中放置
accJDialog.setLocationRelativeTo(null);
//设置关闭后进行其他操作
accJDialog.setModal(true);
//设置弹窗可视
accJDialog.setVisible(true);
} else if (source == animalItem) {
//随机更换动物图片
int rAnimalNum = r.nextInt(8) + 1;
path = "PuzzleGame\\image\\animal\\animal" + rAnimalNum + "\\";
//重新初始化数据
initData();
//计数器归0
stepCount = 0;
//重新根据初始化后的数据加载图片
loadImages();
} else if (source == girllItem) {
//随机更换美女图片
int rGirlNum = r.nextInt(13) + 1;
path = "PuzzleGame\\image\\girl\\girl" + rGirlNum + "\\";
//重新初始化数据
initData();
//计数器归0
stepCount = 0;
//重新根据初始化后的数据加载图片
loadImages();
} else if (source == sportItem) {
//随机更换运动图片
int rSportlNum = r.nextInt(10) + 1;
path = "PuzzleGame\\image\\sport\\sport" + rSportlNum + "\\";
//重新初始化数据
initData();
//计数器归0
stepCount = 0;
//重新根据初始化后的数据加载图片
loadImages();
}
}
//键盘监听KeyListener
//该方法基本不使用
@Override
public void keyTyped(KeyEvent e) {
}
//长按键盘时调用该方法
@Override
public void keyPressed(KeyEvent e) {
//判断是否胜利
if (victoty()) {
//如果胜利,禁止进行显示全图操作,直接结束方法
return;
}
//长按w显示全图
int keyCode = e.getKeyCode();
if (keyCode == 87) {
//清空所有图片
this.getContentPane().removeAll();
//加载计数器
JLabel countJLabel = new JLabel("步数:" + stepCount);
countJLabel.setBounds(50, 30, 100, 20);
this.getContentPane().add(countJLabel);
//加载全图
JLabel picJLabel = new JLabel(new ImageIcon(path + "all.jpg"));
picJLabel.setBounds(83, 134, 420, 420);
this.getContentPane().add(picJLabel);
//添加背景图片
JLabel bgJLabel = new JLabel(new ImageIcon("PuzzleGame\\image\\background.png"));
bgJLabel.setBounds(40, 40, 508, 560);
this.getContentPane().add(bgJLabel);
//刷新一下
this.getContentPane().repaint();
}
}
//按下并松开键盘时调用该方法
@Override
public void keyReleased(KeyEvent e) {
//判断是否胜利
if (victoty()) {
//如果胜利,禁止进行移动操作,直接结束方法
return;
}
int keyCode = e.getKeyCode();
if (keyCode == 37) {
//判断是否到了最左边
if (y == 0) {
//如果到了最左边,不能进行向左,直接结束方法
return;
}
//没到最左边,进行下面的操作
System.out.println("向左");
//更改data数组里的数据
data[x][y] = data[x][y - 1];
data[x][y - 1] = 0;
//更改0的坐标
y--;
//步数加1
stepCount++;
//按照此数据重新加载图片
loadImages();
} else if (keyCode == 38) {
//判断是否到了最上边
if (x == 0) {
//如果到了最上边,不能进行向上,直接结束方法
return;
}
//没到最上边,进行下面的操作
System.out.println("向上");
//更改data数组里的数据
data[x][y] = data[x - 1][y];
data[x - 1][y] = 0;
//更改0的坐标
x--;
//步数加1
stepCount++;
//按照此数据重新加载图片
loadImages();
} else if (keyCode == 39) {
//判断是否到了最右边
if (y == 3) {
//如果到了最右边,不能进行向右,直接结束方法
return;
}
//没到最右边,进行下面的操作
System.out.println("向右");
//更改data数组里的数据
data[x][y] = data[x][y + 1];
data[x][y + 1] = 0;
//更改0的坐标
y++;
//步数加1
stepCount++;
//按照此数据重新加载图片
loadImages();
} else if (keyCode == 40) {
//判断是否到了最下边
if (x == 3) {
//如果到了最下边,不能进行向下,直接结束方法
return;
}
//没到最下边,进行下面的操作
System.out.println("向下");
//更改data数组里的数据
data[x][y] = data[x + 1][y];
data[x + 1][y] = 0;
//更改0的坐标
x++;
//步数加1
stepCount++;
//按照此数据重新加载图片
loadImages();
} else if (keyCode == 87) {
//松开w恢复原状
loadImages();
} else if (keyCode == 65) {
//一键通过
for (int i = 0; i < data.length; i++) {
for (int j = 0; j < data[i].length; j++) {
data[i][j] = winArr[i][j];
}
}
loadImages();
}
}
}
生成验证码的工具类
import java.util.Random;
public class CodeUtil {
private CodeUtil(){}
public static String getCode(){
//定义StringBuilder方便拼接字符串
StringBuilder sb = new StringBuilder();
//记录a~z,A~Z
char[] ch = new char[52];
for (int i = 0; i < ch.length; i++) {
if (i < 26) {
ch[i] = (char) ('a' + i);
} else {
ch[i] = (char) ('A' + i - 26);
}
}
Random r = new Random();
//随机抽取4个字母
for (int i = 0; i < 4; i++) {
int index = r.nextInt(ch.length);
char randomCh = ch[index];
//拼接
sb.append(randomCh);
}
//随机抽取1个字母
int randomNum = r.nextInt(10);
//拼接
sb.append(randomNum);
//数字可以位于随机位置,因此最后一位需要与随机位置交换
char[] chars = sb.toString().toCharArray();
int index = r.nextInt(chars.length);
char temp = chars[chars.length-1];
chars[chars.length-1] = chars[index];
chars[index] = temp;
return new String(chars);
}
}
用户类(封装用户名,密码)
public class User {
private String userName;
private String passWord;
public User() {
}
public User(String userName, String passWord) {
this.userName = userName;
this.passWord = passWord;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassWord() {
return passWord;
}
public void setPassWord(String passWord) {
this.passWord = passWord;
}
}