1、主界面分析

(1)任务一

由于我的分辨率和老师的不一样,因此我将以上像素都×2,生成的大小差不多。
解题思路:首先可以分别写三个类继承JFeame,分别对应游戏主界面、登录界面和注册界面,之后使用构造函数进行初始化,使用setsize和setvisible将显示框的宽高和是否显示进行设置。之后在src下创建App类,用于启动项目,需要那个对象就创建那个
代码如下:

App:
import com.xzhao.test7.ui.GameGFrame;
import com.xzhao.test7.ui.LoginFrame;
import com.xzhao.test7.ui.RegisterFrame;
public class App {
public static void main(String[] args) {
//程序的启动窗口
//如果要开启那个页面,就创建谁的对象即可
new GameGFrame();//创建游戏主界面
new LoginFrame();//创建登录界面
new RegisterFrame();//创建注册界面
}
}
RegisterFrame:
package com.xzhao.test7.ui;
import javax.swing.*;
public class RegisterFrame extends JFrame {
//注册界面
public RegisterFrame() {
this.setSize(976,1000);
this.setVisible(true);
}
}
LoginFrame:
package com.xzhao.test7.ui;
import javax.swing.*;
public class LoginFrame extends JFrame {
//登录界面
public LoginFrame(){
this.setSize(976,860);
this.setVisible(true);
}
}
GameFrame:
package com.xzhao.test7.ui;
import javax.swing.*;
public class GameGFrame extends JFrame {
//游戏的主界面,构造函数相当于初始化,设置了页面的宽高和显示页面
public GameGFrame() {
this.setSize(1206, 1360);//设置宽高
this.setVisible(true);//显示界面,不然默认界面是关闭的
}
}
结果展示:

(2)完善主界面的设置
以主界面代码为例:
this.setTitle("拼图小游戏1.0");//设置界面的标题
this.setAlwaysOnTop(true);//将游戏界面永远置于顶层,可以盖住任意软件
this.setLocationRelativeTo(null);//设置界面居中
this.setDefaultCloseOperation(3);//当关闭这个界面程序就停止,关闭模式选择3
在上述情况下加入这几行代码,放在可视化界面代码前面即可,其中关于关闭模式主要有以下:
等于2表示只有关闭最后一个界面程序才停止,但要在所有界面中设置关闭模式为2;
等于3表示只要关闭一个界面程序(整个虚拟机)就停止;
由于在实际操作中,不会同时出现3个界面,因此设置为3是合理的。

结果展示:

2、菜单制作



直接调用方法即可
代码如下:
package com.xzhao.test7.ui;
import javax.swing.*;
public class GameGFrame extends JFrame {
//游戏的主界面,构造函数相当于初始化,设置了页面的宽高和显示页面
public GameGFrame() {
init();//界面设置
menu();//菜单设置
this.setVisible(true);//显示界面,不然默认界面是关闭的
}
private void menu() {
//初始化菜单
JMenuBar jmenubar=new JMenuBar();//创建整个菜单对象
JMenu functionmenu=new JMenu("功能");//创建选项
JMenu aboutmenu=new JMenu("关于我们");
JMenuItem refreshitem=new JMenuItem("重新游戏");//创建具体条目
JMenuItem refreshlogin=new JMenuItem("重新登录");
JMenuItem closegame=new JMenuItem("关闭游戏");
JMenuItem accountitem=new JMenuItem("公众号");
functionmenu.add(refreshitem);//将具体的条目放到相应的选项中
functionmenu.add(refreshlogin);
functionmenu.add(closegame);
aboutmenu.add(accountitem);
jmenubar.add(functionmenu);//将选项加到菜单对象中
jmenubar.add(aboutmenu);
this.setJMenuBar(jmenubar);//将菜单对象添加到界面中
}
private void init() {
this.setSize(1206, 1360);//设置宽高
this.setTitle("拼图小游戏1.0");//设置界面的标题
this.setAlwaysOnTop(true);//将游戏界面永远置于顶层,可以盖住任意软件
this.setLocationRelativeTo(null);//设置界面居中
this.setDefaultCloseOperation(3);//当关闭这个界面程序就停止,关闭模式选择3
}
}
由于构造函数中的内容过多,因此ctrl+alt+m 将选中内容提取为一个方法,提取之后改名字+enter即可,便于程序的可读性。
结果如下:

3、添加图片
(1)不是将一张图片直接放进去,而是通过将图片分割成15个Imagelcon以及一个空白格,生成

解题思路:先创建Imagelcon对象,放到JLabel(可设置图片的宽高和位置)中,之后再将这个放到界面中
图片素材放在文章中了,有需要的可以使用!
代码如下:
private void initimage() {//初始化图片
ImageIcon imageicon=new ImageIcon("C:\\Users\\xzhao\\IdeaProjects\\test\\image\\animal\\animal3\\3.jpg");//创建图片对象
JLabel label=new JLabel(imageicon);//创建标签对象,将图片添加到标签中
this.add(label);//将标签添加到界面中,默认居中
}
结果如下:

(2)指定图片到相应的位置,首先设置原点,以左上角为原点

之后插入的图片坐标以左上角为主

要将图片加到隐藏的容器中,即getContentPane中,此外为了取消图片默认居中,首先要在界面初始化时将其取消,在init()方法中添加一句:
this.setLayout(null);//将界面中的图片默认居中取消,否则图片一直默认居中放置
之后将initinage方法改成以下内容:
private void initimage() {//初始化图片
ImageIcon imageicon=new ImageIcon("C:\\Users\\xzhao\\IdeaProjects\\test\\image\\animal\\animal3\\1.jpg");//创建图片对象
JLabel label=new JLabel(imageicon);//创建标签对象,将图片添加到标签中
label.setBounds(0, 0, imageicon.getIconWidth(), imageicon.getIconHeight());
//this.add(label);//将标签添加到界面中,默认居中
this.getContentPane().add(label);//将标签添加到界面中
}
结果如下:

(3)添加16张图片,使用循环嵌套的方式
private void initimage() {//初始化图片
//内循环:一行要添加4行图片
//外循环:一共添加4行
int number=1;//更改图片的计数器
for(int j=0;j<4;j++){
for(int i=0;i<4;i++){
JLabel label=new JLabel(new ImageIcon("C:\\Users\\xzhao\\IdeaProjects\\test\\image\\animal\\animal3\\"+number+".jpg"));//创建标签对象,将图片添加到标签中
label.setBounds(105*i, 105*j, 105, 105);
this.getContentPane().add(label);//将标签添加到界面中
number++;//当number=16时,由于找不到图片,因此自动添加了一个空白
}
}
}
首先要对图片进行循环嵌套展示,之后使用拼接的方法对使用那张图片进行修改
结果如下:

(4)打乱图片
将原来的顺序进行随机打乱,然后将4个数放一维数组,一共放4个,使用二维数组去放置。


先做一个练习:
int[] tempArr={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
for(int i=tempArr.length;i>0;i--){
Random rand = new Random();
int randomIndex = rand.nextInt(i)+1;
int temp = tempArr[i - 1];
tempArr[i - 1] = tempArr[randomIndex];
tempArr[randomIndex] = temp;
}
int[][] data=new int[4][4];//创建二维数组
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
data[i][j]=tempArr[i*4+j];
}
}
实现以上功能,之后将以上内容添加到游戏界面中,将以上内容添加到初始化图片之前,这样在初始化图片时就可以直接输出
首先将二维数组在最开始建立,因为除了初始化数据要用,初始化图片也要用
之后修改代码:
private void initdata() {//初始化图片数据
int[] tempArr={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
for(int i=tempArr.length;i>0;i--){
Random rand = new Random();
int randomIndex = rand.nextInt(i)+1;
int temp = tempArr[i - 1];
tempArr[i - 1] = tempArr[randomIndex];
tempArr[randomIndex] = temp;
}
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
data[i][j]=tempArr[i*4+j];
}
}
}
并将初始化图片中的number删去,可以直接用data[i][j]代替。
结果如下:

4、事件

实现动作监听的两种方法:
a、使用匿名内部类
JFrame frame = new TestDemo();
frame.setTitle("测试窗口");
frame.setSize(500,500);
frame.setLocationRelativeTo(null);//居中显示
frame.setDefaultCloseOperation(3);//设置关闭模式
frame.setLayout(null);
JButton btn = new JButton("测试按钮");//建立一个按钮
btn.setBounds(0,0,105,105);//设置按钮的位置
btn.addActionListener(new ActionListener() {//使用匿名内部类对按钮进行实现,空格或者鼠标点击时触发动作监听事件
@Override
public void actionPerformed(ActionEvent e) {
System.out.println("按钮被点击了");
}
});
frame.getContentPane().add(btn);//把按钮添加到界面中
frame.setVisible(true);//设置可见
这种方式使用鼠标点击按钮或者空格时,控制端显示 按钮被点击了
b、使用实现接口方式进行重写,对两个按钮进行
按钮实现类:
package com.xzhao.test2;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Random;
public class Myjm extends JFrame implements ActionListener {
JButton btn1 = new JButton("点我");//建立一个按钮
JButton btn2 = new JButton("再点我");//建立一个按钮
public Myjm() {
JFrame frame = new TestDemo();
frame.setTitle("测试框");
frame.setSize(500, 500);
frame.setLocationRelativeTo(null);//居中显示
frame.setDefaultCloseOperation(3);//设置关闭模式
frame.setLayout(null);
btn1.setBounds(0, 0, 105, 105);//设置按钮的位置
btn1.addActionListener(this);//将按钮添加到事件监听中
btn2.setBounds(105, 0, 105, 105);//设置按钮的位置
btn2.addActionListener(this);//将按钮添加到事件监听中
frame.add(btn1);
frame.add(btn2);
frame.setVisible(true);//设置可见
}
@Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == btn1) {
btn1.setSize( 200, 200);
}
if (e.getSource() == btn2) {
Random r=new Random();
btn2.setLocation(r.nextInt(420), r.nextInt(420));
}
}
}
主程序:
Myjm myjm = new Myjm();//创建一个测试类的对象
结果如下:



弊端:只能在按下按钮之后实现功能,对于其他操作不能实现!!!
鼠标监听:


新创建一个新的实现类:
package com.xzhao.test2;
import javax.swing.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class Myjm2 extends JFrame implements MouseListener {
public Myjm2() {
JFrame frame = new TestDemo();
frame.setTitle("测试框");
frame.setSize(500, 500);
frame.setLocationRelativeTo(null);//居中显示
frame.setDefaultCloseOperation(3);//设置关闭模式
frame.setLayout(null);
JButton btn1 = new JButton("点我");//建立一个按钮
btn1.setBounds(0, 0, 105, 105);//设置按钮的位置
btn1.addMouseListener(this);//将按钮添加到事件监听中
frame.add(btn1);
frame.setVisible(true);//设置可见
}
@Override
public void mouseClicked(MouseEvent e) {
System.out.println("鼠标点击了" );
}
@Override
public void mousePressed(MouseEvent e) {
System.out.println("鼠标按下了");
}
@Override
public void mouseReleased(MouseEvent e) {
System.out.println("鼠标松开了");
}
@Override
public void mouseEntered(MouseEvent e) {
System.out.println("鼠标进入了");
}
@Override
public void mouseExited(MouseEvent e) {
System.out.println("鼠标退出了");
}
}
主函数:
Myjm2 myjm2 = new Myjm2();//创建一个测试类的对象
结果展示:

键盘监听:KeyListener

主要实现类:
package com.xzhao.test2;
import javax.swing.*;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class Myjm3 extends JFrame implements KeyListener {
public Myjm3() {
JFrame frame = new TestDemo();
frame.setTitle("测试框");
frame.setSize(500, 500);
frame.setLocationRelativeTo(null);//居中显示
frame.setDefaultCloseOperation(3);//设置关闭模式
frame.setLayout(null);
JButton btn = new JButton("点我");//建立一个按钮
btn.setBounds(0, 0, 105, 105);//设置按钮的位置
btn.addKeyListener(this);//将按钮添加到事件监听中
frame.add(btn);
frame.setVisible(true);//设置可见
}
@Override
public void keyTyped(KeyEvent e) {//这个一般不用,不用写
}
@Override//当按下不松时,会一直调用这个程序
public void keyPressed(KeyEvent e) {
System.out.println("键盘按下不松");
}
@Override
public void keyReleased(KeyEvent e) {
System.out.println("键盘松开了");
if(e.getKeyCode()==65){//getkeycode可以获取键盘上的编号,本身与ascll码没关系,只是一个编号
System.out.println("A键被按下");
}
if(e.getKeyCode()==68){
System.out.println("D键被按下");
}
}
}
主程序:
Myjm3 myjm3 = new Myjm3();//创建一个测试类的对象
结果显示:

5、美化界面
首先将加载的15个图片放到页面中下(此处修改setBounds),之后添加背景图(将其添加到15张图片引入之后),最后给图片添加边框(添加setborder)。
并将绝对路径更改为相对路径(非盘符开始)
private void initimage() {//初始化图片
//内循环:一行要添加4行图片
//外循环:一共添加4行
for(int j=0;j<4;j++){
for(int i=0;i<4;i++){
JLabel label=new JLabel(new ImageIcon("image\\animal\\animal3\\"+data[i][j]+".jpg"));//创建标签对象,将图片添加到标签中
label.setBounds(105*i+83, 105*j+104, 105, 105);
//给图片添加边框,0表示让图片凸起来,1表示让图片凹进去
label.setBorder(new BevelBorder(0));
this.getContentPane().add(label);//将标签添加到界面中
// number++;//当number=16时,由于找不到图片,因此自动添加了一个空白
}
}
JLabel labelb=new JLabel(new ImageIcon("image\\background.png"));
labelb.setBounds(40, 10, 508, 560);
this.getContentPane().add(labelb);//先加载的图片在上方,后加载的在下方
}
结果如下:

6、移动图片


首先在类中添加键盘监听的接口
之后定义两个值,用于后续记录空白图片的横纵坐标
之后更改初始化图片数据,记录空白图片坐标
private void initdata() {//初始化图片数据
int[] tempArr={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0};
for(int i=tempArr.length;i>0;i--){
Random rand = new Random();
int randomIndex = rand.nextInt(i)+1;
int temp = tempArr[i - 1];
tempArr[i - 1] = tempArr[randomIndex];
tempArr[randomIndex] = temp;
}
for(int i=0;i<tempArr.length;i++){
if(tempArr[i]==0){
x=i/4;
y=i%4;
}
else{
data[i/4][i%4]=tempArr[i];
}
}
}
在初始化图片的开头和结尾分别添加清空和刷新界面
private void initimage() {//初始化图片
//清除已经出现的图片
this.getContentPane().removeAll();//清除所有图片,重新生成
//内循环:一行要添加4行图片
//外循环:一共添加4行
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
JLabel label=new JLabel(new ImageIcon(path+data[i][j]+".jpg"));//创建标签对象,将图片添加到标签中
label.setBounds(105*j+83, 105*i+104, 105, 105);
//给图片添加边框,0表示让图片凸起来,1表示让图片凹进去
label.setBorder(new BevelBorder(0));
this.getContentPane().add(label);//将标签添加到界面中
// number++;//当number=16时,由于找不到图片,因此自动添加了一个空白
}
}
之后对重写接口的内容进行添加,在这里数组是先纵坐标再横坐标,因此需要在代码中注意!
为了防止边界超出索引,因此添加并行事件,防止出bug
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
int keyCode = e.getKeyCode();//获取用户按下的键值
if(keyCode==KeyEvent.VK_LEFT&&y<3){//如果用户按下的是向左键
data[x][y]=data[x][y+1];
data[x][y+1]=0;
y++;//将0的横坐标加1
initimage();//重新初始化图片
System.out.println("向左移动");
}
else if(keyCode==KeyEvent.VK_RIGHT&&y>0){
data[x][y]=data[x][y-1];
data[x][y-1]=0;
y--;//将0的横坐标减1
initimage();//重新初始化图片
System.out.println("向右移动");
}
else if(keyCode==KeyEvent.VK_UP&&x<3){
data[x][y]=data[x+1][y];
data[x+1][y]=0;
x++;//将0的纵坐标加1
initimage();//重新初始化图片
System.out.println("向上移动");
}
else if(keyCode==KeyEvent.VK_DOWN&&x>0){
data[x][y]=data[x-1][y];
data[x-1][y]=0;
x--;//将0的纵坐标减1
initimage();//重新初始化图片
System.out.println("向下移动");
}
最终结果可以实现上下左右移动图片。
7、查看完整图片
按下A键显示完整图片,使用键盘事件,首先在按下哪里编写程序
@Override
public void keyPressed(KeyEvent e) {
int key=e.getKeyCode();
if(key==65){
this.getContentPane().removeAll();//清除所有图片,重新生成
JLabel labelb1=new JLabel(new ImageIcon(path+"all.jpg"));//加载完整图片
labelb1.setBounds(38, 38, 508, 560);
this.getContentPane().add(labelb1);
JLabel labelb2=new JLabel(new ImageIcon("image\\background.png"));//加载背景图片
labelb2.setBounds(40, 10, 508, 560);
this.getContentPane().add(labelb2);
this.getContentPane().repaint();//刷新界面,将新添加的图片显示出来
}
}
之后在松开哪里添加一段代码
else if(keyCode==65){
initimage();
}
为了方便后续更换图片,在这里使用字符串将path进行定义
String path="image\\animal\\animal7\\";
后续直接用就可以,如下所示,是按下A显示的图片

8、作弊码

一定要主要i对应行,j对应列,但是图片左上角坐标的x则是先按列走,y是行!要注意这里!
直接在松开哪里添加一个键盘按键的作用,直接给data赋新值,
else if(keyCode==66){//如果是B,松开就一键胜利
System.out.println("胜利");
data=new int[][]{
{1,2,3,4},
{5,6,7,8},
{9,10,11,12},
{13,14,15,0}
};//直接将排序好的赋给data就可以
x=3;
y=3;
initimage();
}
9、判断胜利

首先建立一个数组,存放正确的图片顺序
int[][] windata=new int[][]{//设置成功的二维数据
{1,2,3,4},
{5,6,7,8},
{9,10,11,12},
{13,14,15,0}
};
之后定义一个方法,返回布尔值,只有全部正确flag才是true
public boolean win() {
boolean flag=true;
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
if(data[i][j]!=windata[i][j]){
flag=false;
}
}
}
return flag;
}
只有返回true才代表胜利
接下来在两个地方进行判断:第一个是在图片初始化中,清空图片之后添加以下代码,保证胜利的图片在最上面!
if(win()){//如果胜利则显示胜利的图片
JLabel label=new JLabel(new ImageIcon("image\\win.png"));
label.setBounds(200, 260, 197, 73);
this.getContentPane().add(label);
}
第二个是在重写的方法移动按钮上添加判断,此处return直接结束方法
if(win()){//如果胜利则不允许移动
return;
}
通过以上可以实现功能。
10、记步

定义一个成员变量,如果定义在方法内,容易导致每次调用方法都重新赋值为0
int count=0;//记录步数
将以下代码加到初始化图片中,使用JLabel进行文字的显示
JLabel step=new JLabel("步数:"+count);
step.setBounds(40, 15, 100, 30);
this.getContentPane().add(step);
之后在每个移动中写一个count++,写在初始化图片前面,不然会比实际步数少1!
结果如下:

11、重新开始

由于只需要一个单击的作用,选择动作监听更好。
首先将条目信息需要挪到成员变量中
JMenuItem refreshitem=new JMenuItem("重新游戏");//创建具体条目
JMenuItem refreshlogin=new JMenuItem("重新登录");
JMenuItem closegame=new JMenuItem("关闭游戏");
JMenuItem accountitem=new JMenuItem("公众号");
之后在menu中分别给以上内容绑定事件
refreshitem.addActionListener(this);//将动作事件分别绑定
refreshlogin.addActionListener(this);
closegame.addActionListener(this);
accountitem.addActionListener(this);
最后在重写的动作事件中添加以下代码
if(e.getSource()==refreshitem){
count=0;
initdata();
initimage();
}
12、关闭游戏

else if(e.getSource()==closegame){
System.exit(0);
}
13、关于我们
可以找一个二维码放到这里,将图片在imagelcon中加载,然后在jlabel中放置,最后放在jdialog中
代码如下:
else if(e.getSource()==accountitem){
JDialog dialog=new JDialog(this,"公众号");//创建弹框对象
dialog.setSize(350,350);
dialog.setLocationRelativeTo(null);
JLabel labelb=new JLabel(new ImageIcon("image\\about.png"));
labelb.setBounds(50,50,258,258);
dialog.getContentPane().add(labelb);
dialog.setModal(true);//弹窗不关闭无法进行下面的操作
dialog.setVisible(true);
}
14、添加更换图片的功能
首先在menu方法中添加,这里jmenu可以添加另一个jmenu对象,因此,如下所示,将更换图片这个添加到功能中即可,此外要记得给更换图片中的三个条目:动物、女孩和运动绑定动作事件,同样将其设置为成员变量:
JMenuItem animals=new JMenuItem("动物");
JMenuItem girls=new JMenuItem("女孩");
JMenuItem sports=new JMenuItem("运动");
private void menu() {
//初始化菜单
JMenuBar jmenubar=new JMenuBar();//创建整个菜单对象
JMenu functionmenu=new JMenu("功能");//创建选项
JMenu aboutmenu=new JMenu("关于我们");
JMenu changepic=new JMenu("更换图片");
functionmenu.add(refreshitem);//将具体的条目放到相应的选项中
functionmenu.add(refreshlogin);
functionmenu.add(closegame);
functionmenu.add(changepic);//在功能中添加更换图片,jmenu和添加到另一个jmenu中
aboutmenu.add(accountitem);
changepic.add(animals);
changepic.add(girls);
changepic.add(sports);
refreshitem.addActionListener(this);//将动作事件分别绑定
refreshlogin.addActionListener(this);
closegame.addActionListener(this);
accountitem.addActionListener(this);
animals.addActionListener(this);
girls.addActionListener(this);
sports.addActionListener(this);
jmenubar.add(functionmenu);//将选项加到菜单对象中
jmenubar.add(aboutmenu);
this.setJMenuBar(jmenubar);//将菜单对象添加到界面中
}
最后在动作事件中,添加具体的实现方法,主要通过更改path路径即可,此处使用了random随机生成编号,然后导入,可实现该功能!!!
else if(e.getSource()==animals){
int animalsnum=random.nextInt(8)+1;
path="image\\animal\\animal"+animalsnum+"\\";
initdata();
initimage();
}else if(e.getSource()==girls){
int girlnum=random.nextInt(13)+1;
path="image\\girl\\girl"+girlnum+"\\";
initdata();
initimage();
}else if(e.getSource()==sports){
int sportnum=random.nextInt(10)+1;
path="image\\sport\\sport"+sportnum+"\\";
initdata();
initimage();
}
15、登录界面


界面代码如下所示:
public class LoginJFrame extends JFrame {
//创建一个集合存储正确的用户名和密码
static ArrayList<User> list = new ArrayList<>();
static {
list.add(new User("zhangsan","123"));
list.add(new User("lisi","1234"));
}
public LoginJFrame() {
//初始化界面
initJFrame();
//在这个界面中添加内容
initView();
//让当前界面显示出来
this.setVisible(true);
}
public void initView() {
//1. 添加用户名文字
JLabel usernameText = new JLabel(new ImageIcon("puzzlegame\\image\\login\\用户名.png"));
usernameText.setBounds(116, 135, 47, 17);
this.getContentPane().add(usernameText);
//2.添加用户名输入框
JTextField 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.密码输入框
JTextField 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);
//验证码的输入框
JTextField code = new JTextField();
code.setBounds(195, 256, 100, 30);
this.getContentPane().add(code);
String codeStr = CodeUtil.getCode();
JLabel rightCode = new JLabel();
//设置内容
rightCode.setText(codeStr);
//位置和宽高
rightCode.setBounds(300, 256, 50, 30);
//添加到界面
this.getContentPane().add(rightCode);
//5.添加登录按钮
JButton 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.添加注册按钮
JButton 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);
}
public void initJFrame() {
this.setSize(488, 430);//设置宽高
this.setTitle("拼图游戏 V1.0登录");//设置标题
this.setDefaultCloseOperation(3);//设置关闭模式
this.setLocationRelativeTo(null);//居中
this.setAlwaysOnTop(true);//置顶
this.setLayout(null);//取消内部默认布局
}
//要展示用户名或密码错误
public void showJDialog(String content) {
//创建一个弹框对象
JDialog jDialog = new JDialog();
//给弹框设置大小
jDialog.setSize(200, 150);
//让弹框置顶
jDialog.setAlwaysOnTop(true);
//让弹框居中
jDialog.setLocationRelativeTo(null);
//弹框不关闭永远无法操作下面的界面
jDialog.setModal(true);
//创建Jlabel对象管理文字并添加到弹框当中
JLabel warning = new JLabel(content);
warning.setBounds(0, 0, 200, 150);
jDialog.getContentPane().add(warning);
//让弹框展示出来
jDialog.setVisible(true);
}
}
登录界面,代码如下:
package com.xzhao.test7.ui;
import javax.swing.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
public class LoginFrame extends JFrame implements MouseListener{
//登录界面
//创建一个集合存储正确的用户名和密码
static ArrayList<User> list = new ArrayList<>();
static {
list.add(new User("zhangsan","123"));
list.add(new User("lisi","1234"));
}
JButton login=new JButton();//登录按钮成员变量
JButton register=new JButton();
JTextField username = new JTextField();//用户名
JPasswordField password = new JPasswordField();//密码
JTextField code = new JTextField();//输入的验证码
JLabel rightCode = new JLabel();//正确的验证码
public LoginFrame() {
//初始化界面
initJFrame();
//在这个界面中添加内容
initView();
//让当前界面显示出来
this.setVisible(true);
}
public void initView() {
//1. 添加用户名文字
JLabel usernameText = new JLabel(new ImageIcon("image\\login\\用户名.png"));
usernameText.setBounds(116, 135, 47, 17);
this.getContentPane().add(usernameText);
//2.添加用户名输入框
username.setBounds(195, 134, 200, 30);
this.getContentPane().add(username);
//3.添加密码文字
JLabel passwordText = new JLabel(new ImageIcon("image\\login\\密码.png"));
passwordText.setBounds(130, 195, 32, 16);
this.getContentPane().add(passwordText);
//4.密码输入框
password.setBounds(195, 195, 200, 30);
this.getContentPane().add(password);
//验证码提示
JLabel codeText = new JLabel(new ImageIcon("image\\login\\验证码.png"));
codeText.setBounds(133, 256, 50, 30);
this.getContentPane().add(codeText);
//验证码的输入框
code.setBounds(195, 256, 100, 30);
this.getContentPane().add(code);
String codeStr = CodeUtil.getCode();
//设置内容
rightCode.setText(codeStr);
//绑定鼠标事件
rightCode.addMouseListener(this);
//位置和宽高
rightCode.setBounds(300, 256, 50, 30);
//添加到界面
this.getContentPane().add(rightCode);
//5.添加登录按钮
login.setBounds(123, 310, 128, 47);
login.setIcon(new ImageIcon("image\\login\\登录按钮.png"));
//去除按钮的边框
login.setBorderPainted(false);
//去除按钮的背景
login.setContentAreaFilled(false);
//给登录按钮绑定鼠标事件
login.addMouseListener(this);
this.getContentPane().add(login);
//6.添加注册按钮
register.setBounds(256, 310, 128, 47);
register.setIcon(new ImageIcon("image\\login\\注册按钮.png"));
//去除按钮的边框
register.setBorderPainted(false);
//去除按钮的背景
register.setContentAreaFilled(false);
//给注册按钮绑定鼠标事件
register.addMouseListener(this);
this.getContentPane().add(register);
//7.添加背景图片
JLabel background = new JLabel(new ImageIcon("image\\login\\background.png"));
background.setBounds(0, 0, 470, 390);
this.getContentPane().add(background);
}
public void initJFrame() {
this.setSize(488, 430);//设置宽高
this.setTitle("拼图游戏 V1.0登录");//设置标题
this.setDefaultCloseOperation(3);//设置关闭模式
this.setLocationRelativeTo(null);//居中
this.setAlwaysOnTop(true);//置顶
this.setLayout(null);//取消内部默认布局
}
//要展示用户名或密码错误
public void showJDialog(String content) {
//创建一个弹框对象
JDialog jDialog = new JDialog();
//给弹框设置大小
jDialog.setSize(200, 150);
//让弹框置顶
jDialog.setAlwaysOnTop(true);
//让弹框居中
jDialog.setLocationRelativeTo(null);
//弹框不关闭永远无法操作下面的界面
jDialog.setModal(true);
//创建Jlabel对象管理文字并添加到弹框当中
JLabel warning = new JLabel(content);
warning.setBounds(0, 0, 200, 150);
jDialog.getContentPane().add(warning);
//让弹框展示出来
jDialog.setVisible(true);
}
@Override
public void mouseClicked(MouseEvent e) {
if (e.getSource() == login) {
System.out.println("登录开始");
String usernameStr = username.getText();//获取用户输入的用户名
String passwordStr = password.getText();
String codeStr = code.getText();
User user = new User(usernameStr, passwordStr);
System.out.println("输入的用户名为:" + usernameStr);
System.out.println("输入的密码为:" + passwordStr);
if (codeStr == null) {
showJDialog("验证码不能为空!");
} else if ((usernameStr.length() == 0) || (passwordStr.length() == 0)) {
showJDialog("用户名或密码不能为空!");
} else if (!codeStr.equalsIgnoreCase(rightCode.getText())) {
showJDialog("验证码错误!");
} else if (checkUser(user)) {
showJDialog("登录成功!");
this.setVisible(false);//关闭登录界面
new GameGFrame();//打开游戏的主界面
} else {
System.out.println("用户名或密码错误");
showJDialog("用户名或密码错误");
}
} else if (e.getSource() == register) {
System.out.println("注册按钮");
} else if (e.getSource() == rightCode) {
System.out.println("更换验证码");
String codeStr = CodeUtil.getCode();
rightCode.setText(codeStr);
}
}
@Override
public void mousePressed(MouseEvent e) {
if(e.getSource() == login) {
login.setIcon(new ImageIcon("image\\login\\登录按下.png"));
}else if(e.getSource() == register) {
register.setIcon(new ImageIcon("image\\login\\注册按下.png"));
}
}
@Override
public void mouseReleased(MouseEvent e) {
if(e.getSource() == login) {
login.setIcon(new ImageIcon("image\\login\\登录按钮.png"));
}else if(e.getSource() == register) {
register.setIcon(new ImageIcon("image\\login\\注册按钮.png"));
}
}
@Override
public void mouseEntered(MouseEvent e) {
}
@Override
public void mouseExited(MouseEvent e) {
}
public boolean checkUser(User userinput){
for(int i=0;i<list.size();i++){
User u=list.get(i);
if(u.getName().equals(userinput.getName())&&u.getPassword().equals(userinput.getPassword())){
return true;
}
}
return false;
}
}
以下是游戏界面全部代码:
package com.xzhao.test7.ui;
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 GameGFrame extends JFrame implements KeyListener, ActionListener {
//游戏的主界面,构造函数相当于初始化,设置了页面的宽高和显示页面
int[][] data=new int[4][4];//创建二维数组,用于管理图片的位置,可以控制上下左右移动
int x=0;//记录图片0对应的横坐标
int y=0;//记录图片0对应的纵坐标
String path="image\\animal\\animal5\\";
int[][] windata=new int[][]{//设置成功的二维数据
{1,2,3,4},
{5,6,7,8},
{9,10,11,12},
{13,14,15,0}
};
int count=0;//记录步数
JMenuItem refreshitem=new JMenuItem("重新游戏");//创建具体条目
JMenuItem refreshlogin=new JMenuItem("重新登录");
JMenuItem closegame=new JMenuItem("关闭游戏");
JMenuItem animals=new JMenuItem("动物");
JMenuItem girls=new JMenuItem("女孩");
JMenuItem sports=new JMenuItem("运动");
JMenuItem accountitem=new JMenuItem("公众号");
public GameGFrame() {
init();//初始化界面
menu();//初始化菜单
initdata();//初始化数据(将图片数据进行打乱)
initimage();//初始化图片
this.setVisible(true);//显示界面,不然默认界面是关闭的
}
public boolean win() {
boolean flag=true;
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
if(data[i][j]!=windata[i][j]){
flag=false;
}
}
}
return flag;
}
private void initdata() {//初始化图片数据
int[] tempArr={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,0};//将0放到最后可以在每次运行时将空白放到新的位置
Random rand = new Random();
for(int i=tempArr.length;i>0;i--){
int randomIndex = rand.nextInt(i);//注意此处的索引值最大是15!!!
int temp = tempArr[i - 1];
tempArr[i - 1] = tempArr[randomIndex];
tempArr[randomIndex] = temp;
}
for(int i=0;i<tempArr.length;i++){
if(tempArr[i]==0){
x=i/4;
y=i%4;
}
data[i/4][i%4]=tempArr[i];
}
}
private void initimage() {//初始化图片
//清除已经出现的图片
this.getContentPane().removeAll();//清除所有图片,重新生成
if(win()){//如果胜利则显示胜利的图片
JLabel label=new JLabel(new ImageIcon("image\\win.png"));
label.setBounds(200, 260, 197, 73);
this.getContentPane().add(label);
}
//内循环:一行要添加4行图片
//外循环:一共添加4行
for(int i=0;i<4;i++){
for(int j=0;j<4;j++){
JLabel label=new JLabel(new ImageIcon(path+data[i][j]+".jpg"));//创建标签对象,将图片添加到标签中
label.setBounds(105*j+83, 105*i+104, 105, 105);
//给图片添加边框,0表示让图片凸起来,1表示让图片凹进去
label.setBorder(new BevelBorder(0));
this.getContentPane().add(label);//将标签添加到界面中
// number++;//当number=16时,由于找不到图片,因此自动添加了一个空白
}
}
JLabel step=new JLabel("步数:"+count);
step.setBounds(40, 15, 100, 30);
this.getContentPane().add(step);
JLabel labelb=new JLabel(new ImageIcon("image\\background.png"));
labelb.setBounds(40, 10, 508, 560);
this.getContentPane().add(labelb);//先加载的图片在上方,后加载的在下方
this.getContentPane().repaint();//刷新界面,将新添加的图片显示出来
}
private void menu() {
//初始化菜单
JMenuBar jmenubar=new JMenuBar();//创建整个菜单对象
JMenu functionmenu=new JMenu("功能");//创建选项
JMenu aboutmenu=new JMenu("关于我们");
JMenu changepic=new JMenu("更换图片");
functionmenu.add(refreshitem);//将具体的条目放到相应的选项中
functionmenu.add(refreshlogin);
functionmenu.add(closegame);
functionmenu.add(changepic);//在功能中添加更换图片,jmenu和添加到另一个jmenu中
aboutmenu.add(accountitem);
changepic.add(animals);
changepic.add(girls);
changepic.add(sports);
refreshitem.addActionListener(this);//将动作事件分别绑定
refreshlogin.addActionListener(this);
closegame.addActionListener(this);
accountitem.addActionListener(this);
animals.addActionListener(this);
girls.addActionListener(this);
sports.addActionListener(this);
jmenubar.add(functionmenu);//将选项加到菜单对象中
jmenubar.add(aboutmenu);
this.setJMenuBar(jmenubar);//将菜单对象添加到界面中
}
private void init() {
this.setSize(603, 680);//设置宽高
this.setTitle("拼图小游戏1.0");//设置界面的标题
this.setAlwaysOnTop(true);//将游戏界面永远置于顶层,可以盖住任意软件
this.setLocationRelativeTo(null);//设置界面居中
this.setDefaultCloseOperation(3);//当关闭这个界面程序就停止,关闭模式选择3
this.setLayout(null);//将界面中的图片默认居中取消,否则图片一直默认居中放置
this.addKeyListener(this);//将键盘事件添加到界面中
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
int key=e.getKeyCode();
if(key==65){//如果是A,按下显示完整图片
this.getContentPane().removeAll();//清除所有图片,重新生成
JLabel labelb1=new JLabel(new ImageIcon(path+"all.jpg"));//加载完整图片
labelb1.setBounds(38, 38, 508, 560);
this.getContentPane().add(labelb1);
JLabel labelb2=new JLabel(new ImageIcon("image\\background.png"));//加载背景图片
labelb2.setBounds(40, 10, 508, 560);
this.getContentPane().add(labelb2);
this.getContentPane().repaint();//刷新界面,将新添加的图片显示出来
}
}
@Override
public void keyReleased(KeyEvent e) {
if(win()){//如果胜利则不允许移动
return;
}
int keyCode = e.getKeyCode();//获取用户按下的键值
if(keyCode==KeyEvent.VK_LEFT&&y<3){//如果用户按下的是向左键
data[x][y]=data[x][y+1];
data[x][y+1]=0;
y++;//将0的横坐标加1
count++;
initimage();//重新初始化图片
System.out.println("向左移动");
}
else if(keyCode==KeyEvent.VK_RIGHT&&y>0){
data[x][y]=data[x][y-1];
data[x][y-1]=0;
y--;//将0的横坐标减1
count++;
initimage();//重新初始化图片
System.out.println("向右移动");
}
else if(keyCode==KeyEvent.VK_UP&&x<3){
data[x][y]=data[x+1][y];
data[x+1][y]=0;
x++;//将0的纵坐标加1
count++;
initimage();//重新初始化图片
System.out.println("向上移动");
}
else if(keyCode==KeyEvent.VK_DOWN&&x>0){
data[x][y]=data[x-1][y];
data[x-1][y]=0;
x--;//将0的纵坐标减1
count++;
initimage();//重新初始化图片
System.out.println("向下移动");
}
else if(keyCode==65){//如果是A,松开显示原来的图片
initimage();
}
else if(keyCode==66){//如果是B,松开就一键胜利
System.out.println("胜利");
data=new int[][]{
{1,2,3,4},
{5,6,7,8},
{9,10,11,12},
{13,14,15,0}
};//直接将排序好的赋给data就可以
x=3;
y=3;
initimage();
}
}
@Override
public void actionPerformed(ActionEvent e) {
Random random=new Random();//随机给定任意一个图片的编号
if(e.getSource()==refreshitem){
count=0;//实现计数器清0
initdata();
initimage();
}else if(e.getSource()==refreshlogin){
this.setVisible(false);//关闭游戏界面
new LoginFrame();//重新打开登录界面
}else if(e.getSource()==closegame){
System.exit(0);
}else if(e.getSource()==accountitem){
JDialog dialog=new JDialog(this,"公众号");//创建弹框对象
dialog.setSize(350,350);
dialog.setLocationRelativeTo(null);
JLabel labelb=new JLabel(new ImageIcon("image\\about.png"));
labelb.setBounds(50,50,258,258);
dialog.getContentPane().add(labelb);
dialog.setModal(true);//弹窗不关闭无法进行下面的操作
dialog.setVisible(true);
}else if(e.getSource()==animals){
int animalsnum=random.nextInt(8)+1;
path="image\\animal\\animal"+animalsnum+"\\";
initdata();
initimage();
}else if(e.getSource()==girls){
int girlnum=random.nextInt(13)+1;
path="image\\girl\\girl"+girlnum+"\\";
initdata();
initimage();
}else if(e.getSource()==sports){
int sportnum=random.nextInt(10)+1;
path="image\\sport\\sport"+sportnum+"\\";
initdata();
initimage();
}
}
}
注册界面因为需要用到后边的知识,所以这里没有写太多
package com.xzhao.test7.ui;
import javax.swing.*;
public class RegisterFrame extends JFrame {
//注册界面
public RegisterFrame() {
this.setSize(488,500);
this.setTitle("注册界面");//设置界面的标题
this.setAlwaysOnTop(true);//将游戏界面永远置于顶层,可以盖住任意软件
this.setLocationRelativeTo(null);//设置界面居中
this.setDefaultCloseOperation(3);//当关闭这个界面程序就停止,关闭模式选择3
this.setVisible(true);
}
}
主界面:
import com.xzhao.test7.ui.LoginFrame;
public class App {
public static void main(String[] args) {
//程序的启动窗口
//如果要开启那个页面,就创建谁的对象即可
//new GameGFrame();//创建游戏主界面
new LoginFrame();//创建登录界面
// new RegisterFrame();//创建注册界面
}
}
结果展示:
效果展示
运行这个代码之后会先跳出登录界面,因为注册还没有写,因此这个目前只能登录已经写好的两个对象,一个是"zhangsan",123;另一个是"lisi",1234。通过以上代码也是对游戏的一些开发有了一定的了解,第一次感受到项目。