哎,最近被这个搞的头晕眼花的,烦死了!!!
不多解释了,代码注释很详细(毕竟写了好几天还熬了个大✌夜),不会欢迎私聊(虽然本人JAVA真的拉)
MainJFrame.java//主窗体(package zhangjinyang;大家自己换包名)
package zhangjinyang;
import javax.swing.*;
import java.awt.Color;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.filechooser.FileNameExtensionFilter;
public class MainJFrame extends JFrame implements Runnable{
public static void main(String[]args)
{
new MainJFrame();
}
//相当于初始化--------------------
JMenuBar jmenubar;//菜单栏
JMenu menu,menuclass,menuhelp;//菜单(菜单、等级、帮助)
JMenuItem itembegin,itemend,itemchange,itemabout,itemrecord,itemclear;//菜单项(开始、退出、图片更换、关于游戏、游戏记录、清空记录)
JRadioButtonMenuItem itemeasy,itemnormal,itemhard;//单选菜单项(简单、一般、困难)
JLabel total_time,total_step;//标签(步数,时间)
//相当于初始化--------------------
JPanel jp;//主面板
LeftJPanel lp;//左面板
RightJPanel rp;//右面板
URL url;//定位符,查找图片使用
//起止时间
long startTime,endTime;
//创建线程对象,实现计时功能
Thread th;
// Label total_count;
//设置主窗口样式----------------------------
public MainJFrame()
{
//整体的设置---------------
setTitle("拼图游戏");
setSize(1440,780);
setLocationRelativeTo(null);//null表示固定相对位置在屏幕中间
setResizable(false);//可调整大小(false不可调整大小)
setDefaultCloseOperation(EXIT_ON_CLOSE);//运行完之后,点关闭,自动停止程序运行
setVisible(true);//设置窗口可见
//添加界面菜单栏---------------
menuinit();//菜单初始化
//面板初始化----------------
init();
//先创建个运行线程------------------
th=new Thread(this);
//开始菜单监视器
itembegin.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//启动线程
//如果线程没启动,调用start方法
if(!th.isAlive()) th.start();
startTime=System.currentTimeMillis();//当前时间毫秒
rp.times=0;//步数为0
rp.randomOrder();//每次都要调动自动打乱命令
}
});
//结束游戏
itemend.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
System.exit(1);
}
});
//难易程度
itemeasy.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
rp.hs=2;//传递2*2到右面板
rp.ls=2;
//初始化组件(init就是美小格进行计算和定位)
rp.init(url);
}
});
itemnormal.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
rp.hs=3;
rp.ls=3;
rp.init(url);
}
});
itemhard.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
rp.hs=4;
rp.ls=4;
rp.init(url);
}
});
//游戏记录显示
itemrecord.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//info用来储存要存放的记录
String info="";
try {
File f=new File("F:\\游戏记录.dat");
if(f.exists())
{
//创建输入流
FileReader fr=new FileReader("F:\\游戏记录.dat");
//用来读取数据
char[] chs=new char[1024];
int len;
while ((len=fr.read(chs))!=-1)
{
//读取的结果放在info中
// char[]值,
//int偏移量,
//整数计数
info+=new String(chs,0,len);
}
fr.close();//读完关闭
//通过消息框显示结果(作业窗格)
JOptionPane.showMessageDialog(null,info);
}
else{
JOptionPane.showMessageDialog(null,"游戏记录为空!");
}
// } catch (FileNotFoundException fileNotFoundException) {
// fileNotFoundException.printStackTrace();
} catch (IOException e1) {
e1.printStackTrace();//主要的不是很理解我就知道和rightJPanel里面异常追捕对应,其实我觉得不对应也行
}
}
});
//关于游戏
itemabout.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null,"关于拼图游戏\r\n版本:v2.0\r\n作者:XiaoZhang\r\n欢迎进入游戏!");
}
});
//清空记录
itemclear.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
File f=new File("F:\\游戏记录.dat");
if(f.exists())
f.delete();
}
});
//图片进行更换
itemchange.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
//显示一个打开对话框,选择一个图片文件,将文件转换成url对象,调用左右面板的相应方法
JFileChooser jfc=new JFileChooser();//文件选择器
//设置文件筛选器 文件名扩展筛选器
jfc.setFileFilter(new FileNameExtensionFilter("图片格式(jpg|png|gif|jpeg)", "jpg","png","gif","jpeg"));
//弹出打开对话框
int sd=jfc.showOpenDialog(MainJFrame.this);
if(sd==jfc.APPROVE_OPTION)//如果用户选择了打开按钮
{
//获取用户选择的文件(获取绝对路径)
String file=jfc.getSelectedFile().getAbsolutePath();
try {
url=new URL("file:\\"+file);//获取新图片但是为啥这样表示,等我再研究研究
//更新两个面板
lp.init(url);
rp.init(url);
} catch (MalformedURLException e1) {
e1.printStackTrace();
}
}
}
});
}
//设置整个窗口-----------------------------------
public void init() {
//相当于赋初值-------------------
jp=new JPanel();
lp=new LeftJPanel();
rp=new RightJPanel();
url=this.getClass().getResource("1000.jpg");
//分割主面板(网格布局分割为一行两列,一边放lp,一边放rp)
jp.setLayout(new GridLayout(1,2));
jp.add(lp);
jp.add(rp);
lp.init(url);
rp.init(url);
//再将主面板放到主窗体,把拼图拼好挂墙上,一个意思
this.add(jp);//如果我没猜错this一般代指最高层就是窗口
}
//设置整个菜单样式----------------------------
public void menuinit() {
//相当于赋初值-------------------
jmenubar = new JMenuBar();
menu=new JMenu("菜单");
menuclass=new JMenu("等级");
menuhelp=new JMenu("帮助");
itembegin=new JMenuItem("开始游戏");
itemend=new JMenuItem("结束游戏");
itemchange=new JMenuItem("更换图片");
itemabout=new JMenuItem("关于游戏");
itemrecord=new JMenuItem("游戏记录");
itemclear=new JMenuItem("清空记录");
itemeasy=new JRadioButtonMenuItem("简单");
itemnormal=new JRadioButtonMenuItem("一般");
itemhard=new JRadioButtonMenuItem("困难");
total_step=new JLabel("步数:");
total_time=new JLabel("用时:");
//设置选项的以下小装饰
itemeasy.setSelected(true);//最开始默认先择最简单的
total_time.setForeground(Color.GRAY);//设置标签颜色
total_step.setForeground(Color.gray);
//把单选难度进行按钮组,实现一对多-------------
ButtonGroup group=new ButtonGroup();
group.add(itemeasy);
group.add(itemnormal);
group.add(itemhard);
//把按钮都添加到菜单的菜单里-------------
menu.add(itembegin);
menu.add(itemend);
menu.add(itemchange);
//把难度系数添加到菜单的难度等级里面------------
//这里就会有同学疑惑了,慢慢就把这三个放到一个按钮组里面了
///呢直接把按钮组放进来不就可以吗?为什么一个一个加
//其实Java里面规定的记住就行,嘿嘿
menuclass.add(itemeasy);
menuclass.add(itemnormal);
menuclass.add(itemhard);
//把记录等事项添加进菜单的帮助按钮-----------
menuhelp.add(itemabout);
menuhelp.add(itemrecord);
menuhelp.add(itemclear);
//这就相当于把组件都拼好了,然后拼一起一个意思(按顺序拼)
jmenubar.add(menu);
jmenubar.add(menuclass);
jmenubar.add(menuhelp);
jmenubar.add(new JLabel(" "));
jmenubar.add(total_step);
jmenubar.add(new JLabel(" "));
jmenubar.add(total_time);
//这一步就更好说了,把拼图拼好挂墙上
this.setJMenuBar(jmenubar);
th=new Thread(this);
}
@Override
public void run() {//是Runnable的继承
while(true) {
endTime=System.currentTimeMillis();//当前时间毫秒
total_time.setText("用时:"+(endTime-startTime)/1000+"秒");//显示文本
total_step.setText("步数:第"+rp.times+"步");
try {
Thread.sleep(500);
}catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
RightJPanel.java
package zhangjinyang;
import javax.imageio.ImageIO;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.net.URL;
import java.awt.Image;
import java.io.FileWriter;
import java.util.Random;
import java.io.IOException;//缓冲读取数据
//java.io.IOException
public class RightJPanel extends JPanel implements ActionListener
{
int width=700;
int height=700;//面板的大小
JButton[] jbs;//定义按钮数组
int hs=2,ls=2;//设置分割的行列数
int widthbut,heightbut;//按钮的宽度和高度,指定是小图图片缩放的尺寸
int widthtp,heighttp; //图片原始高度宽度
int widthxt,heightxt;//小图的原始宽度高度
int times;//实现步数计算的变量
JButton kb;//空白按钮
public RightJPanel()
{
setLayout(null);//面板布局为空
setSize(width,height);//面板的大小
}
//设计右面板
public void init(URL url)
{
this.removeAll();//面板组件初始化前,先清除所有已有的组件
//创建按钮组
jbs=new JButton[hs*ls];
//为每一个按钮实现初始化
//计算按钮的宽度和高度
widthbut=width/ls;
heightbut=height/hs;
BufferedImage buf=null;//缓冲图片区为空
try {
buf = ImageIO.read(url);//缓冲读取图片
//获取原图的宽度、高度
widthtp=buf.getWidth();
heighttp=buf.getHeight();
//获取小图的宽度和高度(分割后的)
widthxt=widthtp/ls;
heightxt=heighttp/hs;
//给每个子图,也就是按钮确定位置
for(int i=0;i<jbs.length;i++)
{
jbs[i]=new JButton();//每个小子图都新建按钮
jbs[i].setSize(widthbut,heightbut);
//这点很聪明,因为i相当于线性排序,现在为确定每i个小方格的位置现在是二维的
//横向取余,纵向取整
jbs[i].setLocation((i%ls)*widthbut,(i/ls)*heightbut);
//获取每个小图(获取子映像,现在的图是原图映射到每个小方格的里面)
BufferedImage subimage=buf.getSubimage(i%ls*widthxt,i/ls*heightxt,widthxt,heightxt);
//小图的缩放(把刚刚获取的子图片缩小)
Image image = subimage.getScaledInstance(widthbut, heightbut, 1);
//将小图图片放置到按钮上
jbs[i].setIcon(new ImageIcon(image));
//添加按钮到右面板
this.add(jbs[i]);
//设置图片补课被修改(就是刚开始是图片按钮是不显示的需要按开始按钮)
jbs[i].setEnabled(false);//设置已启用被关闭
jbs[i].addActionListener((ActionListener) this);
}
jbs[hs*ls-1].setIcon(null);
kb=jbs[hs*ls-1];
} catch (IOException e) {//IO异常
e.printStackTrace();
}
}
//打乱按钮顺序
public void randomOrder()
{
Random rand=new Random();//创建随机对象
//多次打乱-------------
for(int i=0;i<hs*ls;i++)
{
int index1=rand.nextInt(hs*ls);//bound就是范围
int index2=rand.nextInt(hs*ls);
int x1=jbs[index1].getX();
int y1=jbs[index1].getY();
int x2=jbs[index2].getX();
int y2=jbs[index2].getY();
jbs[index1].setLocation(x2,y2);
jbs[index2].setLocation(x1,y1);
//本来我还在想为啥为啥要设两个随机数,其实还是不太明白,可能要满足(x1,y1)与(x2,y2)重合
jbs[i].setEnabled(true);//将图片显现出来
}
}
@Override
public void actionPerformed(ActionEvent e) {
//获取用户单击的按钮 ,通过ActionEvent e的方法gerSource获取事件源
JButton jb=(JButton)(e.getSource());
//获取单击按钮和空白按钮的位置
int x1=jb.getX();
int y1=jb.getY();
int x2=kb.getX();
int y2=kb.getY();
//判断是否可以被移动
//Math.abs(x1-x2)/widthbut+Math.abs(y1-y2)==1
//给大家说一下我的理解,如果没记错Math.abs是取绝对值
//当两个按钮相邻要么同一行要么同一列,而且是相邻所以要么行号差一,要么列号差一
if(Math.abs(x1-x2)/widthbut+Math.abs(y1-y2)/heightbut==1)
{
jb.setLocation(x2,y2);
kb.setLocation(x1,y1);//换换位置
times++;//步数加一
}
//每走一步就要判断拼图是否拼成功-------------
//成功之后怎么做
if(isWin()){
JOptionPane.showMessageDialog(null, "恭喜你,拼图成功");//判断父组件,这个我也不是很懂说什么帧啥的
//停止按键的使用
for(int i=0;i<jbs.length;i++){
jbs[i].setEnabled(false);
}
//记录信息
String name = JOptionPane.showInputDialog("请输入你的姓名:");
//小tips”\t“是回车符,”\n“是换行
String info=hs+"*"+ls+"拼图记录:"+name+"的步数"+times+"\r\n";
JOptionPane.showMessageDialog(null,info);
try
{
FileWriter fw=new FileWriter("F:\\游戏记录.dat",true);
fw.write(info);//把每次记录都记录下来
fw.close();
}
catch (IOException e1)
{
e1.printStackTrace();
}
}
}
public boolean isWin() {
//获取每个按钮的坐标
for(int i=0;i<jbs.length;i++){
//jbs[i].setLocation((i%ls)*widthbut, i/ls*heightbut);由之前坐标设置给出下面的x,y
int x=jbs[i].getX()/widthbut;
int y=jbs[i].getY()/heightbut;
if(i%ls!=x||i/ls!=y)
return false;
}
return true;
}
}
LeftJPanel.java
package zhangjinyang;
import java.net.URL;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
//左面板类
public class LeftJPanel extends JPanel{
JLabel jl;//左面板
int width=700;
int height=700;
//相当于左窗口的设置-------------------
public LeftJPanel()
{
setSize(width,height);
jl=new JLabel();
jl.setSize(width,height);//窗口和面板也可以设不一样的
//把面板放左窗口里面---------
this.add(jl);
}
public void init(URL url)
{
ImageIcon icon=new ImageIcon(url);//图像图标初始化
//没猜错的话因该是把图片缩放到窗口大小
jl.setIcon(new ImageIcon(icon.getImage().getScaledInstance(width,height,1)));
//刷新界面(验证所有的东西都放入了)
validate();
}
}