java练习--拼图游戏之主游戏界面

项目代码架构:

GameFrame是对游戏主体的设置

LoginFrame是对登录的设置

GameRun是游戏启动设置

Tool是工具类

先设计游戏主窗体:

GameRun类的代码:

import ayue.ui.GameFrameTest;

public class GameRun {
    public static void main(String[] args) {
        new GameFrame(580, 608, "拼图单机版 v1.0");
    }
}
import javax.swing.*;
import java.awt.*;

public class GameFrame extends JFrame {

    /**
     * 游戏主窗体构造器
     * width: 窗体宽  单位px
     * height:窗体高
     * title:标题
     */
    public GameFrame(int width, int height, String title){
        // 设置窗体样式
        this.initFrame(width, height, title);


        // 让页面显示
        this.setVisible(true);
    }

    // 窗体显示设置
    private void initFrame(int width, int height, String title){
        // 设置尺寸
        this.setSize(width, height);
        // 设置窗体的标题
        this.setTitle(title);
        // 设置屏幕在正中心
        this.setLocationRelativeTo(null);
        // 设置页面关闭模式
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        
    }
}

结果:

添加菜单栏:使用JFrame中的setJMenuBar

源码:

java练习--拼图游戏之主游戏界面_第1张图片

 有关菜单的组件大概是:菜单栏  menu bar、菜单 menu、菜单项 menu item、单选按钮菜单项 radio button menu item、复选框菜单项 check box menu items 和分隔符 separators

本游戏中用到的是:菜单栏  menu bar、菜单 menu、菜单项 menu item

三者的关系是:菜单项加载在菜单里,菜单加载在菜单栏里

对应的三个类是:JMenuBar、JMenu、JMenuItem

JMenuBar的描述:An implementation of a menu bar. You add JMenu objects to the menu bar to construct a menu. When the user selects a JMenu object, its associated JPopupMenu is displayed, allowing the user to select one of the JMenuItems on it.

JMenuBar添加菜单的源码 

java练习--拼图游戏之主游戏界面_第2张图片

JMenu中添加JMenuItem

java练习--拼图游戏之主游戏界面_第3张图片

菜单相关类的继承层次结构图:

java练习--拼图游戏之主游戏界面_第4张图片  GameFrame

import javax.swing.*;
import java.awt.*;

public class GameFrame extends JFrame {

    /**
     * 游戏主窗体构造器
     * width: 窗体宽  单位px
     * height:窗体高
     * title:标题
     */
    public GameFrame(int width, int height, String title){
        // 设置窗体样式
        this.initFrame(width, height, title);

        // 添加菜单栏
        this.setJMenuBar(getJMenuBars());

        // 让页面显示
        this.setVisible(true);
    }

    // 窗体显示设置
    private void initFrame(int width, int height, String title){
        // 设置尺寸
        this.setSize(width, height);
        // 设置窗体的标题
        this.setTitle(title);
        // 设置屏幕在正中心
        this.setLocationRelativeTo(null);
        // 设置页面关闭模式
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    // 设置菜单
    private JMenuBar getJMenuBars(){
        // 创建JMenuBar对象
        JMenuBar jMenuBar = new JMenuBar();

        // 创建菜单
        JMenu functionMenu = new JMenu("功能");

        // 创建选项中的三个小的条目
        JMenuItem replayItem = new JMenuItem("重新游戏");
        JMenuItem reLoginItem = new JMenuItem("重新登录");
        JMenuItem closeItem = new JMenuItem("关闭游戏");
        // 将条目添加到选项里
        functionMenu.add(replayItem);
        functionMenu.add(reLoginItem);
        functionMenu.add(closeItem);

        JMenu aboutMenu = new JMenu("关于我们");
        JMenuItem accountItem = new JMenuItem("公众号");
        aboutMenu.add(accountItem);

        // 把菜单添加到菜单栏
        jMenuBar.add(functionMenu);
        jMenuBar.add(aboutMenu);

        return jMenuBar;
    }
}

效果图:

 

添加图片

添加图片需要用到对象:ImageIcon

ImageIcon其中两个构造器:

java练习--拼图游戏之主游戏界面_第5张图片

因为ImageIcon不是一个组件,所以它要先加载到JLabel中

JLable是一个管理文字和图片的组件:

A display area for a short text string or an image, or both.

JLabel其中一个构造器:

java练习--拼图游戏之主游戏界面_第6张图片

或者:

java练习--拼图游戏之主游戏界面_第7张图片 也可以使用setIcon方法:

java练习--拼图游戏之主游戏界面_第8张图片

JFrame、RootPane、ContentPane 

在把JLabel加载到窗体中时需要使用getContentPane方法:

JFrame中的getContentPane方法

java练习--拼图游戏之主游戏界面_第9张图片

 JFrame中的getRootPane

java练习--拼图游戏之主游戏界面_第10张图片

 从上面的截图中可以看出RootPane是JFrame的属性

java练习--拼图游戏之主游戏界面_第11张图片

 RootPane是根窗格、ContentPane是内容窗格

在官方文档中这样描述:

Each top-level container relies on a reclusive intermediate container called the root pane. The root pane manages the content pane and the menu bar, along with a couple of other containers. You generally don't need to know about root panes to use Swing components. However, if you ever need to intercept mouse clicks or paint over multiple components, you should get acquainted with root panes.

每个顶层的容器都依赖于一个被称为根窗格的隐蔽的中间容器。根窗格管理着内容窗格和菜单栏,以及其他几个容器。你一般不需要了解根窗格来使用Swing组件。然而,如果你需要拦截鼠标点击或在多个组件上作画,你应该熟悉根窗格

以下是根窗格提供给框架(以及所有其他顶级容器)的组件列表:

java练习--拼图游戏之主游戏界面_第12张图片

We've already told you about the content pane and the optional menu bar. The two other components that a root pane adds are a layered pane and a glass pane. The layered pane contains the menu bar and content pane, and enables Z-ordering of other components. The glass pane is often used to intercept input events occuring over the top-level container, and can also be used to paint over multiple components.

我们已经向您介绍了内容窗格和可选菜单栏。根窗格添加的另外两个组件是分层窗格和玻璃窗格。分层窗格包含菜单栏和内容窗格,并启用其他组件的 Z 排序。玻璃窗格通常用于拦截发生在顶级容器上的输入事件,也可用于绘制多个组件。

 JRootPane的组件属性:

java练习--拼图游戏之主游戏界面_第13张图片

添加背景图片

import javax.swing.*;
import java.awt.*;

public class GameFrame extends JFrame {

    /**
     * 游戏主窗体构造器
     * width: 窗体宽  单位px
     * height:窗体高
     * title:标题
     */
    public GameFrame(int width, int height, String title){
        // 设置窗体样式
        this.initFrame(width, height, title);

        // 添加菜单栏
        this.setJMenuBar(getJMenuBars());

        // 添加背景图片
        this.addBackgroundImage();

        // 让页面显示
        this.setVisible(true);
    }

    // 窗体显示设置
    private void initFrame(int width, int height, String title){
        // 设置尺寸
        this.setSize(width, height);
        // 设置窗体的标题
        this.setTitle(title);
        // 设置屏幕在正中心
        this.setLocationRelativeTo(null);
        // 设置页面关闭模式
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    }

    // 设置菜单
    private JMenuBar getJMenuBars(){
        // 创建JMenuBar对象
        JMenuBar jMenuBar = new JMenuBar();

        // 创建菜单
        JMenu functionMenu = new JMenu("功能");

        // 创建选项中的三个小的条目
        JMenuItem replayItem = new JMenuItem("重新游戏");
        JMenuItem reLoginItem = new JMenuItem("重新登录");
        JMenuItem closeItem = new JMenuItem("关闭游戏");
        // 将条目添加到选项里
        functionMenu.add(replayItem);
        functionMenu.add(reLoginItem);
        functionMenu.add(closeItem);

        JMenu aboutMenu = new JMenu("关于我们");
        JMenuItem accountItem = new JMenuItem("公众号");
        aboutMenu.add(accountItem);

        // 把菜单添加到菜单栏
        jMenuBar.add(functionMenu);
        jMenuBar.add(aboutMenu);

        return jMenuBar;
    }

    // 添加背景图片
    private void addBackgroundImage(){
        ImageIcon imageIcon = new ImageIcon("puzzlegame/image/background.png");
        JLabel jLabel = new JLabel(imageIcon);
        this.getContentPane().add(jLabel);
    }
}

 运行效果:

 因为在加载JLabel时先添加的会处于上层,因此需要先添加游戏的拼图图片:

试着添加一张:

    // 添加拼图图片
    private void addPuzzleImage(){
        JLabel jLabel = new JLabel(new ImageIcon("puzzlegame/image/animal/animal1/1.jpg"));
        this.getContentPane().add(jLabel);

    }

 效果:

在添加图片时图片会默认居中,如果要使图片在你设置的位置出现就需要取消默认的居中放置,并指定位置

使用setLayout方法设置LayoutManager为null取消居中

默认居中由来:

JFrame

java练习--拼图游戏之主游戏界面_第14张图片

JRootPane

java练习--拼图游戏之主游戏界面_第15张图片 java练习--拼图游戏之主游戏界面_第16张图片

java练习--拼图游戏之主游戏界面_第17张图片

JFrame中的setLayout

java练习--拼图游戏之主游戏界面_第18张图片

 未使用setLayout(null)时设置图片位置

java练习--拼图游戏之主游戏界面_第19张图片

java练习--拼图游戏之主游戏界面_第20张图片 

 

设置取消默认居中

java练习--拼图游戏之主游戏界面_第21张图片

 

加载所有拼图碎片

import javax.swing.*;
import java.awt.*;

public class GameFrame extends JFrame {

    /**
     * 游戏主窗体构造器
     * width: 窗体宽  单位px
     * height:窗体高
     * title:标题
     */
    public GameFrame(int width, int height, String title){
        // 设置窗体样式
        this.initFrame(width, height, title);

        // 添加菜单栏
        this.setJMenuBar(getJMenuBars());

        // 添加拼图图片
        this.addPuzzleImage();

        // 添加背景图片
        //this.addBackgroundImage();

        // 让页面显示
        this.setVisible(true);
    }

    // 窗体显示设置
    private void initFrame(int width, int height, String title){
        // 设置尺寸
        this.setSize(width, height);
        // 设置窗体的标题
        this.setTitle(title);
        // 设置屏幕在正中心
        this.setLocationRelativeTo(null);
        // 设置页面关闭模式
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // 取消默认的居中放置,只有取消了才会按照xy轴指定的位置放置
         this.setLayout(null);
    }

    // 设置菜单
    private JMenuBar getJMenuBars(){
        // 创建JMenuBar对象
        JMenuBar jMenuBar = new JMenuBar();

        // 创建菜单
        JMenu functionMenu = new JMenu("功能");

        // 创建选项中的三个小的条目
        JMenuItem replayItem = new JMenuItem("重新游戏");
        JMenuItem reLoginItem = new JMenuItem("重新登录");
        JMenuItem closeItem = new JMenuItem("关闭游戏");
        // 将条目添加到选项里
        functionMenu.add(replayItem);
        functionMenu.add(reLoginItem);
        functionMenu.add(closeItem);

        JMenu aboutMenu = new JMenu("关于我们");
        JMenuItem accountItem = new JMenuItem("公众号");
        aboutMenu.add(accountItem);

        // 把菜单添加到菜单栏
        jMenuBar.add(functionMenu);
        jMenuBar.add(aboutMenu);

        return jMenuBar;
    }

    // 添加拼图图片
    private void addPuzzleImage(){
        // 循环为每一张拼图碎片创建对象并加入到JLabel中, 图片格局是4x4
        int numImage = 1;
        for (int i = 0; i < 4; i++) {
            for (int j = 0; j < 4; j++) {
                JLabel jLabel = new JLabel(new ImageIcon("/Users/ry/AyueTest/java_test/puzzlegame/image/animal/animal1/"+numImage+".jpg"));
                jLabel.setBounds(0+(j * 105), 0+(i*105), 105, 105);
                this.getContentPane().add(jLabel);
                numImage++;
            }
        }

    }

    // 添加背景图片
    private void addBackgroundImage(){
        ImageIcon imageIcon = new ImageIcon("puzzlegame/image/background.png");
        JLabel jLabel = new JLabel(imageIcon);
        this.getContentPane().add(jLabel);
    }
}

效果图:

 

加上背景图,并使图片位置更合适

java练习--拼图游戏之主游戏界面_第22张图片

打乱图片:

        1. 先为GameFrame类创建一个一维数组的属性,用来存储图片的序号

        2. 写一个函数用来打乱这个数组

        3. 把这个打乱的一维数组以4个为一组创建一个二维数组

        4. 为GameFrame创建一个二维数组的属性,并把上一步生成的数组赋值给它

        5. 以打乱后的二维数组来对图片重新排序展示

Tool.java

import java.util.Random;

public class Tool {
    private static Random random = new Random();

    // 打乱数组顺序
    public static int[] messArray(int[] arr){
        int[] newArr = new int[arr.length];
        // 复制数组
        System.arraycopy(arr, 0, newArr, 0, arr.length);
        for (int i = 0; i < arr.length; i++) {
            int randomIndex = random.nextInt(newArr.length);
            int tmp = newArr[randomIndex];
            newArr[randomIndex] = newArr[i];
            newArr[i] = tmp;
        }
        return newArr;
    }
    
    // 生成二维数组
    public static int[][] getTwoDimensionalArr(int row, int line, int[] arr){
        int[][] twoDimensionalArr = new int[row][line];
        // 解法一:
//        for (int i = 0; i < twoDimensionalArr.length; i++) {
//            System.arraycopy(arr, i*line, twoDimensionalArr[i], 0, line);
//        }
        
        // 解法二:
        for (int i = 0; i < arr.length; i++) {
            int a = i/line;
            int b = i%line;
            if (a < row){
                twoDimensionalArr[a][b] = arr[i];
            }else break;
        }
        return twoDimensionalArr;
    }


}

GameFrame.java


import javax.print.attribute.standard.MediaSize;
import javax.swing.*;
import java.awt.*;

public class GameFrame extends JFrame {
    private int[] oneArr = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
    private int[][] twoArr;


    /**
     * 游戏主窗体构造器
     * width: 窗体宽  单位px
     * height:窗体高
     * title:标题
     */
    public GameFrame(int width, int height, String title){
        // 设置窗体样式
        this.initFrame(width, height, title);

        // 添加菜单栏
        this.setJMenuBar(getJMenuBars());

        // 初始化数据
        this.initDate();

        // 添加拼图图片
        this.addImage();

        // 让页面显示
        this.setVisible(true);
    }

    // 窗体显示设置
    private void initFrame(int width, int height, String title){
        // 设置尺寸
        this.setSize(width, height);
        // 设置窗体的标题
        this.setTitle(title);
        // 设置屏幕在正中心
        this.setLocationRelativeTo(null);
        // 设置页面关闭模式
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // 取消默认的居中放置,只有取消了才会按照xy轴指定的位置放置
         this.setLayout(null);
    }

    // 设置菜单
    private JMenuBar getJMenuBars(){
        // 创建JMenuBar对象
        JMenuBar jMenuBar = new JMenuBar();

        // 创建菜单
        JMenu functionMenu = new JMenu("功能");

        // 创建选项中的三个小的条目
        JMenuItem replayItem = new JMenuItem("重新游戏");
        JMenuItem reLoginItem = new JMenuItem("重新登录");
        JMenuItem closeItem = new JMenuItem("关闭游戏");
        // 将条目添加到选项里
        functionMenu.add(replayItem);
        functionMenu.add(reLoginItem);
        functionMenu.add(closeItem);

        JMenu aboutMenu = new JMenu("关于我们");
        JMenuItem accountItem = new JMenuItem("公众号");
        aboutMenu.add(accountItem);

        // 把菜单添加到菜单栏
        jMenuBar.add(functionMenu);
        jMenuBar.add(aboutMenu);

        return jMenuBar;
    }

    // 添加拼图图片
    private void addImage(){
        // 循环为每一张拼图碎片创建对象并加入到JLabel中, 图片格局是4x4
        // 按照初始化后的数据展示图片
        for (int i = 0; i < this.twoArr.length; i++) {
            for (int j = 0; j < this.twoArr[i].length; j++) {
                JLabel jLabel = new JLabel(new ImageIcon("/Users/ry/AyueTest/java_test/puzzlegame/image/animal/animal1/"+this.twoArr[i][j]+".jpg"));
                jLabel.setBounds(0+(j * 105) + 84, 0+(i*105) + 134, 105, 105);
                this.getContentPane().add(jLabel);
            }
        }

        // 添加背景图片
        addBackgroundImage();

    }

    // 初始化数据
    private void initDate(){ this.twoArr = Tool.getTwoDimensionalArr(4, 4, Tool.messArray(this.oneArr));}

    // 添加背景图片
    private void addBackgroundImage(){
        ImageIcon imageIcon = new ImageIcon("puzzlegame/image/background.png");
        JLabel jLabel = new JLabel(imageIcon);
        jLabel.setBounds(40, 40, 508, 560);
        this.getContentPane().add(jLabel);
    }
}

 效果图:

给图片加上边框:

java练习--拼图游戏之主游戏界面_第23张图片 

增加重新游戏的功能

        为功能里的重新游戏绑定点击事件 

        使用接口:ActionListener  动作监听接口

ActionListener

java练习--拼图游戏之主游戏界面_第24张图片

ActionListener是简易的事件监听,可监听到鼠标的单击和enter键

import javax.print.attribute.standard.MediaSize;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

public class GameFrame extends JFrame implements ActionListener {
    private int[] oneArr = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16};
    private int[][] twoArr;


    /**
     * 游戏主窗体构造器
     * width: 窗体宽  单位px
     * height:窗体高
     * title:标题
     */
    public GameFrame(int width, int height, String title){
        // 设置窗体样式
        this.initFrame(width, height, title);

        // 添加菜单栏
        this.setJMenuBar(getJMenuBars());

        // 初始化数据
        this.initDate();

        // 添加拼图图片
        this.addImage();

        // 让页面显示
        this.setVisible(true);
    }

    // 窗体显示设置
    private void initFrame(int width, int height, String title){
        // 设置尺寸
        this.setSize(width, height);
        // 设置窗体的标题
        this.setTitle(title);
        // 设置屏幕在正中心
        this.setLocationRelativeTo(null);
        // 设置页面关闭模式
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // 取消默认的居中放置,只有取消了才会按照xy轴指定的位置放置
         this.setLayout(null);
    }

    // 设置菜单
    private JMenuBar getJMenuBars(){
        // 创建JMenuBar对象
        JMenuBar jMenuBar = new JMenuBar();

        // 创建菜单
        JMenu functionMenu = new JMenu("功能");

        // 创建选项中的三个小的条目
        JMenuItem replayItem = new JMenuItem("重新游戏");
        // 绑定动作监听
        replayItem.addActionListener(this);
        JMenuItem reLoginItem = new JMenuItem("重新登录");
        JMenuItem closeItem = new JMenuItem("关闭游戏");
        // 将条目添加到选项里
        functionMenu.add(replayItem);
        functionMenu.add(reLoginItem);
        functionMenu.add(closeItem);

        JMenu aboutMenu = new JMenu("关于我们");
        JMenuItem accountItem = new JMenuItem("公众号");
        aboutMenu.add(accountItem);

        // 把菜单添加到菜单栏
        jMenuBar.add(functionMenu);
        jMenuBar.add(aboutMenu);

        return jMenuBar;
    }

    // 添加拼图图片
    private void addImage(){
        // 在重新开始游戏加载图片前,需要把上次的图片组件删除,因为组件后添加的是放在最后的
        this.getContentPane().removeAll();
        // 循环为每一张拼图碎片创建对象并加入到JLabel中, 图片格局是4x4
        // 按照初始化后的数据展示图片
        for (int i = 0; i < this.twoArr.length; i++) {
            for (int j = 0; j < this.twoArr[i].length; j++) {
                JLabel jLabel = new JLabel(new ImageIcon("/Users/ry/AyueTest/java_test/puzzlegame/image/animal/animal1/"+this.twoArr[i][j]+".jpg"));
                jLabel.setBounds(0+(j * 105) + 84, 0+(i*105) + 134, 105, 105);
                // 给图片添加边框
                jLabel.setBorder(BorderFactory.createBevelBorder(1));
                this.getContentPane().add(jLabel);
            }
        }

        // 添加背景图片
        addBackgroundImage();

        // 只删除不行,还需要重绘组件
        this.getContentPane().repaint();

    }

    // 初始化数据
    private void initDate(){ this.twoArr = Tool.getTwoDimensionalArr(4, 4, Tool.messArray(this.oneArr));}

    // 添加背景图片
    private void addBackgroundImage(){
        ImageIcon imageIcon = new ImageIcon("puzzlegame/image/background.png");
        JLabel jLabel = new JLabel(imageIcon);
        jLabel.setBounds(40, 40, 508, 560);
        this.getContentPane().add(jLabel);
    }

    // 事件源上的动作发生就执行相关的代码
    @Override
    public void actionPerformed(ActionEvent e) {
        // 重新打乱数据
        this.initDate();
        // 加载图片
        this.addImage();
    }
}

效果:

 

点击重新游戏后

移动图片

       设置一张图片为空白,通过移动空白格玩拼图

        加载图片时如果图片未找到,这显示空白窗格

        移动图片实际就是交换两个图片的位置,在二维数组中的表示就是交换两个元素的下标

        交换两张图片的位置,最简单的一种方式是给这个窗体加上键盘监听,当每次松开键盘(上下左右键)就使图片移动

        通过移动空白窗格来移动拼图碎片,使拼图碎片得以移动到你想要的位置

GameFrame.java

import javax.print.attribute.standard.MediaSize;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.sql.Timestamp;

public class GameFrame extends JFrame implements ActionListener, KeyListener {
    private int[] oneArr = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0};
    private int[][] twoArr;
    // 记录0的下标
    private int a, b;


    /**
     * 游戏主窗体构造器
     * width: 窗体宽  单位px
     * height:窗体高
     * title:标题
     */
    public GameFrame(int width, int height, String title){
        // 设置窗体样式
        this.initFrame(width, height, title);

        // 添加菜单栏
        this.setJMenuBar(getJMenuBars());

        // 初始化数据
        this.initDate();

        // 添加拼图图片
        this.addImage();

        // 给窗体加上键盘监听
        this.addKeyListener(this);

        // 让页面显示
        this.setVisible(true);
    }

    // 窗体显示设置
    private void initFrame(int width, int height, String title){
        // 设置尺寸
        this.setSize(width, height);
        // 设置窗体的标题
        this.setTitle(title);
        // 设置屏幕在正中心
        this.setLocationRelativeTo(null);
        // 设置页面关闭模式
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // 取消默认的居中放置,只有取消了才会按照xy轴指定的位置放置
         this.setLayout(null);
    }

    // 设置菜单
    private JMenuBar getJMenuBars(){
        // 创建JMenuBar对象
        JMenuBar jMenuBar = new JMenuBar();

        // 创建菜单
        JMenu functionMenu = new JMenu("功能");

        // 创建选项中的三个小的条目
        JMenuItem replayItem = new JMenuItem("重新游戏");
        // 绑定动作监听
        replayItem.addActionListener(this);
        JMenuItem reLoginItem = new JMenuItem("重新登录");
        JMenuItem closeItem = new JMenuItem("关闭游戏");
        // 将条目添加到选项里
        functionMenu.add(replayItem);
        functionMenu.add(reLoginItem);
        functionMenu.add(closeItem);

        JMenu aboutMenu = new JMenu("关于我们");
        JMenuItem accountItem = new JMenuItem("公众号");
        aboutMenu.add(accountItem);

        // 把菜单添加到菜单栏
        jMenuBar.add(functionMenu);
        jMenuBar.add(aboutMenu);

        return jMenuBar;
    }

    // 添加拼图图片
    private void addImage(){
        // 在重新开始游戏加载图片前,需要把上次的图片组件删除,因为组件后添加的是放在最后的
        this.getContentPane().removeAll();
        // 循环为每一张拼图碎片创建对象并加入到JLabel中, 图片格局是4x4
        // 按照初始化后的数据展示图片
        for (int i = 0; i < this.twoArr.length; i++) {
            for (int j = 0; j < this.twoArr[i].length; j++) {
                if (twoArr[i][j] == 0){
                    a = i;
                    b = j;
                }
                JLabel jLabel = new JLabel(new ImageIcon("/Users/ry/AyueTest/java_test/puzzlegame/image/animal/animal1/"+this.twoArr[i][j]+".jpg"));
                jLabel.setBounds(0+(j * 105) + 84, 0+(i*105) + 134, 105, 105);
                // 给图片添加边框
                jLabel.setBorder(BorderFactory.createBevelBorder(1));
                this.getContentPane().add(jLabel);
            }
        }

        // 添加背景图片
        addBackgroundImage();

        // 只删除不行,还需要重绘组件
        this.getContentPane().repaint();

    }

    // 初始化数据
    private void initDate(){ this.twoArr = Tool.getTwoDimensionalArr(4, 4, Tool.messArray(this.oneArr));}

    // 添加背景图片
    private void addBackgroundImage(){
        ImageIcon imageIcon = new ImageIcon("puzzlegame/image/background.png");
        JLabel jLabel = new JLabel(imageIcon);
        jLabel.setBounds(40, 40, 508, 560);
        this.getContentPane().add(jLabel);
    }


    // 事件源上的动作发生就执行相关的代码
    @Override
    public void actionPerformed(ActionEvent e) {
        // 重新打乱数据
        this.initDate();
        // 加载图片
        this.addImage();
    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

    @Override
    public void keyPressed(KeyEvent e) {

    }

    @Override
    public void keyReleased(KeyEvent e) {
        // 捕获键盘,获取按下的键盘的keycode
        int code = e.getKeyCode();
        switch (code){
            case 37:
                // 向左移动一格相当于x轴方向的坐标-1,y轴方向的坐标不变,体现在矩阵中就是列变而行不变,
                // 在二维数组中矩阵中的列就是里层数组的下标,行就是外层数组的下标。在这里我们定义了a为空白窗格的外层下标,b为空白窗格的里层下标
                // 向左移动就是b-1,a不变,意思是向左移动就是这个数组在arr[a]中的移动
                this.moveImage("less", "b");
                break;
            case 39:
                this.moveImage("add", "b");
                break;
            case 38:
                this.moveImage("less", "a");
                break;
            case 40:
                this.moveImage("add", "a");
                break;
        }
    }

    // 图片移动操作单独抽取出来
    private void moveImage(String calculation, String which){
        if (calculation =="add"){
            if (which =="b"){
                if (b+1<4){ // 不能超出界限
                    this.twoArr[a][b] = this.twoArr[a][b+1];
                    this.twoArr[a][++b] = 0;
                }
            }else{
                if (a+1<4){
                    this.twoArr[a][b] = this.twoArr[a+1][b];
                    this.twoArr[++a][b] = 0;
                }
            }
        }else{
            if (which == "b"){
                if (b-1>=0){ // 最小下标不能小于0
                    this.twoArr[a][b] = this.twoArr[a][b-1];
                    this.twoArr[a][--b] = 0;
                }
            }else{
                if (a-1>=0){
                    this.twoArr[a][b] = this.twoArr[a-1][b];
                    this.twoArr[--a][b] = 0;
                }
            }

        }
        this.addImage();

    }

}

java练习--拼图游戏之主游戏界面_第25张图片

java练习--拼图游戏之主游戏界面_第26张图片

添加计步器

GameFrame.java

import javax.print.attribute.standard.MediaSize;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.sql.Timestamp;

public class GameFrame extends JFrame implements ActionListener, KeyListener {
    private int[] oneArr = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0};
    private int[][] twoArr;
    // 记录0的下标
    private int a, b;
    // 记录步数
    private int step;


    /**
     * 游戏主窗体构造器
     * width: 窗体宽  单位px
     * height:窗体高
     * title:标题
     */
    public GameFrame(int width, int height, String title){
        // 设置窗体样式
        this.initFrame(width, height, title);

        // 添加菜单栏
        this.setJMenuBar(getJMenuBars());

        // 初始化数据
        this.initDate();

        // 添加拼图图片
        this.addImage();

        // 给窗体加上键盘监听
        this.addKeyListener(this);

        // 让页面显示
        this.setVisible(true);
    }

    // 窗体显示设置
    private void initFrame(int width, int height, String title){
        // 设置尺寸
        this.setSize(width, height);
        // 设置窗体的标题
        this.setTitle(title);
        // 设置屏幕在正中心
        this.setLocationRelativeTo(null);
        // 设置页面关闭模式
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // 取消默认的居中放置,只有取消了才会按照xy轴指定的位置放置
         this.setLayout(null);
    }

    // 设置菜单
    private JMenuBar getJMenuBars(){
        // 创建JMenuBar对象
        JMenuBar jMenuBar = new JMenuBar();

        // 创建菜单
        JMenu functionMenu = new JMenu("功能");

        // 创建选项中的三个小的条目
        JMenuItem replayItem = new JMenuItem("重新游戏");
        // 绑定动作监听
        replayItem.addActionListener(this);
        JMenuItem reLoginItem = new JMenuItem("重新登录");
        JMenuItem closeItem = new JMenuItem("关闭游戏");
        // 将条目添加到选项里
        functionMenu.add(replayItem);
        functionMenu.add(reLoginItem);
        functionMenu.add(closeItem);

        JMenu aboutMenu = new JMenu("关于我们");
        JMenuItem accountItem = new JMenuItem("公众号");
        aboutMenu.add(accountItem);

        // 把菜单添加到菜单栏
        jMenuBar.add(functionMenu);
        jMenuBar.add(aboutMenu);

        return jMenuBar;
    }

    // 添加拼图图片
    private void addImage(){
        // 在重新开始游戏加载图片前,需要把上次的图片组件删除,因为组件后添加的是放在最后的
        this.getContentPane().removeAll();

        // 显示计步器
        JLabel jLabelStep = new JLabel("步数:" + step);
        jLabelStep.setBounds(20, 20, 100, 50);
        this.getContentPane().add(jLabelStep);
        
        // 循环为每一张拼图碎片创建对象并加入到JLabel中, 图片格局是4x4
        // 按照初始化后的数据展示图片
        for (int i = 0; i < this.twoArr.length; i++) {
            for (int j = 0; j < this.twoArr[i].length; j++) {
                if (twoArr[i][j] == 0){
                    a = i;
                    b = j;
                }
                JLabel jLabel = new JLabel(new ImageIcon("/Users/ry/AyueTest/java_test/puzzlegame/image/animal/animal1/"+this.twoArr[i][j]+".jpg"));
                jLabel.setBounds(0+(j * 105) + 84, 0+(i*105) + 134, 105, 105);
                // 给图片添加边框
                jLabel.setBorder(BorderFactory.createBevelBorder(1));
                this.getContentPane().add(jLabel);
            }
        }

        // 添加背景图片
        addBackgroundImage();

        // 只删除不行,还需要重绘组件
        this.getContentPane().repaint();

    }

    // 初始化数据
    private void initDate(){ this.twoArr = Tool.getTwoDimensionalArr(4, 4, Tool.messArray(this.oneArr));}

    // 添加背景图片
    private void addBackgroundImage(){
        ImageIcon imageIcon = new ImageIcon("puzzlegame/image/background.png");
        JLabel jLabel = new JLabel(imageIcon);
        jLabel.setBounds(40, 40, 508, 560);
        this.getContentPane().add(jLabel);
    }

    // 事件源上的动作发生就执行相关的代码
    @Override
    public void actionPerformed(ActionEvent e) {
        // 重新打乱数据
        this.initDate();
        // 加载图片
        this.addImage();
    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

    @Override
    public void keyPressed(KeyEvent e) {

    }

    @Override
    public void keyReleased(KeyEvent e) {
        // 捕获键盘,获取按下的键盘的keycode
        int code = e.getKeyCode();
        switch (code){
            case 37:
                // 向左移动一格相当于x轴方向的坐标-1,y轴方向的坐标不变,体现在矩阵中就是列变而行不变,
                // 在二维数组中矩阵中的列就是里层数组的下标,行就是外层数组的下标。在这里我们定义了a为空白窗格的外层下标,b为空白窗格的里层下标
                // 向左移动就是b-1,a不变,意思是向左移动就是这个数组在arr[a]中的移动
                this.moveImage("less", "b");
                break;
            case 39:
                this.moveImage("add", "b");
                break;
            case 38:
                this.moveImage("less", "a");
                break;
            case 40:
                this.moveImage("add", "a");
                break;
        }
    }

    // 图片移动操作单独抽取出来
    private void moveImage(String calculation, String which){
        if (calculation =="add"){
            if (which =="b"){
                if (b+1<4){ // 不能超出界限
                    this.twoArr[a][b] = this.twoArr[a][b+1];
                    this.twoArr[a][++b] = 0;
                }
            }else{
                if (a+1<4){
                    this.twoArr[a][b] = this.twoArr[a+1][b];
                    this.twoArr[++a][b] = 0;
                }
            }
        }else{
            if (which == "b"){
                if (b-1>=0){ // 最小下标不能小于0
                    this.twoArr[a][b] = this.twoArr[a][b-1];
                    this.twoArr[a][--b] = 0;
                }
            }else{
                if (a-1>=0){
                    this.twoArr[a][b] = this.twoArr[a-1][b];
                    this.twoArr[--a][b] = 0;
                }
            }

        }
        // 每移动一次步数加1
        step++;
        this.addImage();

    }

}

效果图:

 

一键成功+查看完整图片

import javax.print.attribute.standard.MediaSize;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.sql.Timestamp;
import java.util.Arrays;

public class GameFrame extends JFrame implements ActionListener, KeyListener {
    private int[] oneArr = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0};
    private int[][] twoArr;
    private int[][] winArr = new int[][]{
            {1, 2, 3, 4},
            {5, 6, 7, 8},
            {9, 10, 11, 12},
            {13, 14, 15, 0}
    };
    // 记录0的下标
    private int a, b;
    // 记录步数
    private int step;


    /**
     * 游戏主窗体构造器
     * width: 窗体宽  单位px
     * height:窗体高
     * title:标题
     */
    public GameFrame(int width, int height, String title){
        // 设置窗体样式
        this.initFrame(width, height, title);

        // 添加菜单栏
        this.setJMenuBar(getJMenuBars());

        // 初始化数据
        this.initDate();

        // 添加拼图图片
        this.addImage();

        // 给窗体加上键盘监听
        this.addKeyListener(this);

        // 让页面显示
        this.setVisible(true);
    }

    // 窗体显示设置
    private void initFrame(int width, int height, String title){
        // 设置尺寸
        this.setSize(width, height);
        // 设置窗体的标题
        this.setTitle(title);
        // 设置屏幕在正中心
        this.setLocationRelativeTo(null);
        // 设置页面关闭模式
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // 取消默认的居中放置,只有取消了才会按照xy轴指定的位置放置
         this.setLayout(null);
    }

    // 设置菜单
    private JMenuBar getJMenuBars(){
        // 创建JMenuBar对象
        JMenuBar jMenuBar = new JMenuBar();

        // 创建菜单
        JMenu functionMenu = new JMenu("功能");

        // 创建选项中的三个小的条目
        JMenuItem replayItem = new JMenuItem("重新游戏");
        // 绑定动作监听
        replayItem.addActionListener(this);
        JMenuItem reLoginItem = new JMenuItem("重新登录");
        JMenuItem closeItem = new JMenuItem("关闭游戏");
        // 将条目添加到选项里
        functionMenu.add(replayItem);
        functionMenu.add(reLoginItem);
        functionMenu.add(closeItem);

        JMenu aboutMenu = new JMenu("关于我们");
        JMenuItem accountItem = new JMenuItem("公众号");
        aboutMenu.add(accountItem);

        // 把菜单添加到菜单栏
        jMenuBar.add(functionMenu);
        jMenuBar.add(aboutMenu);

        return jMenuBar;
    }

    // 添加拼图图片
    private void addImage(){
        // 在重新开始游戏加载图片前,需要把上次的图片组件删除,因为组件后添加的是放在最后的
        this.getContentPane().removeAll();

        // 加载胜利图片
        if (isWin()){
            JLabel jLabelWin = new JLabel(new ImageIcon("puzzlegame/image/win.png"));
            jLabelWin.setBounds(193, 300, 197, 73);
            this.getContentPane().add(jLabelWin);
        }

        // 显示计步器
        JLabel jLabelStep = new JLabel("步数:" + step);
        jLabelStep.setBounds(20, 20, 100, 50);
        this.getContentPane().add(jLabelStep);

        // 循环为每一张拼图碎片创建对象并加入到JLabel中, 图片格局是4x4
        // 按照初始化后的数据展示图片
        for (int i = 0; i < this.twoArr.length; i++) {
            for (int j = 0; j < this.twoArr[i].length; j++) {
                if (twoArr[i][j] == 0){
                    a = i;
                    b = j;
                }
                JLabel jLabel = new JLabel(new ImageIcon("puzzlegame/image/animal/animal1/"+this.twoArr[i][j]+".jpg"));
                jLabel.setBounds(0+(j * 105) + 84, 0+(i*105) + 134, 105, 105);
                // 给图片添加边框
                jLabel.setBorder(BorderFactory.createBevelBorder(1));
                this.getContentPane().add(jLabel);
            }
        }

        // 添加背景图片
        addBackgroundImage();

        // 只删除不行,还需要重绘组件
        this.getContentPane().repaint();

    }

    // 判断是否成功
    private boolean isWin(){
        for (int i = 0; i < this.winArr.length; i++) {
            if (!Arrays.equals(this.twoArr[i], this.winArr[i])){
                return false;
            }
        }
        return true;
    }

    // 初始化数据
    private void initDate(){ this.twoArr = Tool.getTwoDimensionalArr(4, 4, Tool.messArray(this.oneArr));}

    // 添加背景图片
    private void addBackgroundImage(){
        ImageIcon imageIcon = new ImageIcon("puzzlegame/image/background.png");
        JLabel jLabel = new JLabel(imageIcon);
        jLabel.setBounds(40, 40, 508, 560);
        this.getContentPane().add(jLabel);
    }

    // 事件源上的动作发生就执行相关的代码
    @Override
    public void actionPerformed(ActionEvent e) {
        // 重新打乱数据
        this.initDate();
        // 加载图片
        this.addImage();
    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

    @Override
    public void keyPressed(KeyEvent e) {
        int code = e.getKeyCode();
        if (code==65){
            // 加载完整图片
            this.getContentPane().removeAll();
            JLabel jLabelAll = new JLabel(new ImageIcon("puzzlegame/image/animal/animal1/all.jpg"));
            jLabelAll.setBounds(84, 104, 420, 420);
            this.getContentPane().add(jLabelAll);
            this.getContentPane().repaint();
        }

    }

    @Override
    public void keyReleased(KeyEvent e) {
        addImage();
        // 捕获键盘,获取按下的键盘的keycode
        int code = e.getKeyCode();
        // 当拼图完成,移动不再有效
        if (isWin()){return;}
        switch (code){
            case 37:
                // 向左移动一格相当于x轴方向的坐标-1,y轴方向的坐标不变,体现在矩阵中就是列变而行不变,
                // 在二维数组中矩阵中的列就是里层数组的下标,行就是外层数组的下标。在这里我们定义了a为空白窗格的外层下标,b为空白窗格的里层下标
                // 向左移动就是b-1,a不变,意思是向左移动就是这个数组在arr[a]中的移动
                this.moveImage("less", "b");
                break;
            case 39:
                this.moveImage("add", "b");
                break;
            case 38:
                this.moveImage("less", "a");
                break;
            case 40:
                this.moveImage("add", "a");
                break;
            case 87:
                this.twoArr = this.winArr;
                addImage();
        }
    }

    // 图片移动操作单独抽取出来
    private void moveImage(String calculation, String which){
        if (calculation =="add"){
            if (which =="b"){
                if (b+1<4){ // 不能超出界限
                    this.twoArr[a][b] = this.twoArr[a][b+1];
                    this.twoArr[a][++b] = 0;
                }
            }else{
                if (a+1<4){
                    this.twoArr[a][b] = this.twoArr[a+1][b];
                    this.twoArr[++a][b] = 0;
                }
            }
        }else{
            if (which == "b"){
                if (b-1>=0){ // 最小下标不能小于0
                    this.twoArr[a][b] = this.twoArr[a][b-1];
                    this.twoArr[a][--b] = 0;
                }
            }else{
                if (a-1>=0){
                    this.twoArr[a][b] = this.twoArr[a-1][b];
                    this.twoArr[--a][b] = 0;
                }
            }

        }
        // 每移动一次步数加1
        step++;
        this.addImage();

    }

}

效果图:

关闭游戏按钮:

1. 给关闭游戏的菜单添加动作事件

2. 由于要在其他函数中用到这个菜单对象,所以把所有的子菜单都设置成属性

3. 点击关闭游戏,当动作监听捕获到的事件源是这个,就关闭游戏窗口和程序

java练习--拼图游戏之主游戏界面_第27张图片

java练习--拼图游戏之主游戏界面_第28张图片

java练习--拼图游戏之主游戏界面_第29张图片 

关于我们

        同关闭游戏一样,也需要绑定点击事件

        点击关于我们,会弹出一个对话框,展示一个二维码图片

        对话框我们需要创建一个JDialog的对象,这个类也是一个顶级窗口,一般用来创建对话框

        代码:

java练习--拼图游戏之主游戏界面_第30张图片

 效果图:

java练习--拼图游戏之主游戏界面_第31张图片

 注意:

        如上代码中,并没有显式的设置弹出框是否是模态框,或者是弹出框的类型,在源码中其实是有设置的:

java练习--拼图游戏之主游戏界面_第32张图片

java练习--拼图游戏之主游戏界面_第33张图片

有关游戏主界面的全部代码:

import javax.print.attribute.standard.MediaSize;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.sql.Timestamp;
import java.util.Arrays;

public class GameFrame extends JFrame implements ActionListener, KeyListener {
    private int[] oneArr = new int[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0};
    private int[][] twoArr;
    private int[][] winArr = new int[][]{
            {1, 2, 3, 4},
            {5, 6, 7, 8},
            {9, 10, 11, 12},
            {13, 14, 15, 0}
    };
    // 记录0的下标
    private int a, b;
    // 记录步数
    private int step;
    // 把菜单(JMenuItem设置成属性)便于在其他方法中使用
    private JMenuItem replayItem, reLoginItem, closeItem, accountItem;


    /**
     * 游戏主窗体构造器
     * width: 窗体宽  单位px
     * height:窗体高
     * title:标题
     */
    public GameFrame(int width, int height, String title){
        // 设置窗体样式
        this.initFrame(width, height, title);

        // 添加菜单栏
        this.setJMenuBar(getJMenuBars());

        // 初始化数据
        this.initDate();

        // 添加拼图图片
        this.addImage();

        // 给窗体加上键盘监听
        this.addKeyListener(this);

        // 让页面显示
        this.setVisible(true);
    }

    // 窗体显示设置
    private void initFrame(int width, int height, String title){
        // 设置尺寸
        this.setSize(width, height);
        // 设置窗体的标题
        this.setTitle(title);
        // 设置屏幕在正中心
        this.setLocationRelativeTo(null);
        // 设置页面关闭模式
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // 取消默认的居中放置,只有取消了才会按照xy轴指定的位置放置
         this.setLayout(null);
    }

    // 设置菜单
    private JMenuBar getJMenuBars(){
        // 创建JMenuBar对象
        JMenuBar jMenuBar = new JMenuBar();

        // 创建菜单
        JMenu functionMenu = new JMenu("功能");

        // 创建选项中的三个小的条目
        replayItem = new JMenuItem("重新游戏");
        // 绑定动作监听
        replayItem.addActionListener(this);

        reLoginItem = new JMenuItem("重新登录");
        reLoginItem.addActionListener(this);

        closeItem = new JMenuItem("关闭游戏");
        closeItem.addActionListener(this);
        // 将条目添加到选项里
        functionMenu.add(replayItem);
        functionMenu.add(reLoginItem);
        functionMenu.add(closeItem);

        JMenu aboutMenu = new JMenu("关于我们");
        accountItem = new JMenuItem("公众号");
        accountItem.addActionListener(this);
        aboutMenu.add(accountItem);

        // 把菜单添加到菜单栏
        jMenuBar.add(functionMenu);
        jMenuBar.add(aboutMenu);

        return jMenuBar;
    }

    // 添加拼图图片
    private void addImage(){
        // 在重新开始游戏加载图片前,需要把上次的图片组件删除,因为组件后添加的是放在最后的
        this.getContentPane().removeAll();

        // 加载胜利图片
        if (isWin()){
            JLabel jLabelWin = new JLabel(new ImageIcon("puzzlegame/image/win.png"));
            jLabelWin.setBounds(193, 300, 197, 73);
            this.getContentPane().add(jLabelWin);
        }

        // 显示计步器
        JLabel jLabelStep = new JLabel("步数:" + step);
        jLabelStep.setBounds(20, 20, 100, 50);
        this.getContentPane().add(jLabelStep);

        // 循环为每一张拼图碎片创建对象并加入到JLabel中, 图片格局是4x4
        // 按照初始化后的数据展示图片
        for (int i = 0; i < this.twoArr.length; i++) {
            for (int j = 0; j < this.twoArr[i].length; j++) {
                if (twoArr[i][j] == 0){
                    a = i;
                    b = j;
                }
                JLabel jLabel = new JLabel(new ImageIcon("puzzlegame/image/animal/animal1/"+this.twoArr[i][j]+".jpg"));
                jLabel.setBounds(0+(j * 105) + 84, 0+(i*105) + 134, 105, 105);
                // 给图片添加边框
                jLabel.setBorder(BorderFactory.createBevelBorder(1));
                this.getContentPane().add(jLabel);
            }
        }

        // 添加背景图片
        addBackgroundImage();

        // 只删除不行,还需要重绘组件
        this.getContentPane().repaint();

    }

    // 判断是否成功
    private boolean isWin(){
        for (int i = 0; i < this.winArr.length; i++) {
            if (!Arrays.equals(this.twoArr[i], this.winArr[i])){
                return false;
            }
        }
        return true;
    }

    // 初始化数据
    private void initDate(){ this.twoArr = Tool.getTwoDimensionalArr(4, 4, Tool.messArray(this.oneArr));}

    // 添加背景图片
    private void addBackgroundImage(){
        ImageIcon imageIcon = new ImageIcon("puzzlegame/image/background.png");
        JLabel jLabel = new JLabel(imageIcon);
        jLabel.setBounds(40, 40, 508, 560);
        this.getContentPane().add(jLabel);
    }

    // 事件源上的动作发生就执行相关的代码
    @Override
    public void actionPerformed(ActionEvent e) {
        Object source = e.getSource();
        if (source == replayItem){  // 重新游戏
            // 重新打乱数据
            this.initDate();
            // 加载图片
            this.addImage();
        }else if (source == closeItem){  // 关闭游戏
            System.exit(0);
        }else if (source == accountItem){  // 关于我们
            // 创建对话框
            JDialog jDialog = new JDialog(this, "二维码");
            // 设置图层永远在最上层
            jDialog.setAlwaysOnTop(true);
            jDialog.setSize(260, 260);
            JLabel jLabel = new JLabel(new ImageIcon("puzzlegame/image/about.png"));
            jLabel.setBounds(0, 0 , 258, 258);
            jDialog.getContentPane().add(jLabel);
            // 设置屏幕在正中心
            jDialog.setLocationRelativeTo(null);
            //
            System.out.println(jDialog.getModalityType());
//            jDialog.setModal();
            jDialog.setVisible(true);
        }else if(source == reLoginItem){  // 重新登录
            // 隐藏GameFrame窗体
            System.out.println("reLoginItem");
            this.removeNotify();
            new LoginFrame(500, 450, "拼图单机版 登录");
        }

    }

    @Override
    public void keyTyped(KeyEvent e) {

    }

    @Override
    public void keyPressed(KeyEvent e) {
        int code = e.getKeyCode();
        if (code==65){
            // 加载完整图片
            this.getContentPane().removeAll();
            JLabel jLabelAll = new JLabel(new ImageIcon("puzzlegame/image/animal/animal1/all.jpg"));
            jLabelAll.setBounds(84, 104, 420, 420);
            this.getContentPane().add(jLabelAll);
            this.getContentPane().repaint();
        }

    }

    @Override
    public void keyReleased(KeyEvent e) {
        addImage();
        // 捕获键盘,获取按下的键盘的keycode
        int code = e.getKeyCode();
        // 当拼图完成,移动不再有效
        if (isWin()){return;}
        switch (code){
            case 37:
                // 向左移动一格相当于x轴方向的坐标-1,y轴方向的坐标不变,体现在矩阵中就是列变而行不变,
                // 在二维数组中矩阵中的列就是里层数组的下标,行就是外层数组的下标。在这里我们定义了a为空白窗格的外层下标,b为空白窗格的里层下标
                // 向左移动就是b-1,a不变,意思是向左移动就是这个数组在arr[a]中的移动
                this.moveImage("less", "b");
                break;
            case 39:
                this.moveImage("add", "b");
                break;
            case 38:
                this.moveImage("less", "a");
                break;
            case 40:
                this.moveImage("add", "a");
                break;
            case 87:
                this.twoArr = this.winArr;
                addImage();
        }
    }

    // 图片移动操作单独抽取出来
    private void moveImage(String calculation, String which){
        if (calculation =="add"){
            if (which =="b"){
                if (b+1<4){ // 不能超出界限
                    this.twoArr[a][b] = this.twoArr[a][b+1];
                    this.twoArr[a][++b] = 0;
                }
            }else{
                if (a+1<4){
                    this.twoArr[a][b] = this.twoArr[a+1][b];
                    this.twoArr[++a][b] = 0;
                }
            }
        }else{
            if (which == "b"){
                if (b-1>=0){ // 最小下标不能小于0
                    this.twoArr[a][b] = this.twoArr[a][b-1];
                    this.twoArr[a][--b] = 0;
                }
            }else{
                if (a-1>=0){
                    this.twoArr[a][b] = this.twoArr[a-1][b];
                    this.twoArr[--a][b] = 0;
                }
            }

        }
        // 每移动一次步数加1
        step++;
        this.addImage();

    }

}

你可能感兴趣的:(java)