07.计步和菜单业务实现

STM32U575RIT6智能手表实战:低功耗与性能平衡的嵌入式设计指南 在嵌入式系统开发中,低功耗与高性能之间的平衡始终是工程师面临的核心挑战。采用ARM Cortex-M33内核的STM32U5系列微控制器,凭借TrustZone安全架构、灵活的Stop2低功耗模式及SMPS供电设计,为智能手表等可穿戴设备提供了一套高效的解决方案。本文从Cortex-M33内核特性切入,分析U5系列在待机电流、运行能效比、外设资源上的优势,并系统讲解基于STM32CubeMX的时钟配置、外设初始化、RTC定时唤醒、传感器数据采集与SPI屏幕驱动实现,同时分享GPIO漏电流排查、动态电压频率调 阅读详情

一.计步功能:

二.计步功能代码实现:

1.代码:

package com.itheima.ui;
​
import javax.swing.*;
import javax.swing.border.BevelBorder;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;
​
public class GameJFrame extends JFrame implements KeyListener {
    /* 注意一定要继承JFrame类
      因为此时要创建一个窗体,而JFrame就是一个界面,窗体,
      继承JFrame的子类也表示界面,窗体
    */
    //规定:GameJFrame这个界面表示的就是游戏的主界面
    //以后跟游戏相关的所有逻辑都写在这个类中
​
​
    //创建一个二维数组
    //目的:用来管理数据
    //加载图片的时候,会根据二维数组中的数据进行加载
    int[][] data=new int[4][4];
​
    //记录空白方块在二维数组中的位置-->一开始设为0
    int x=0;
    int y=0;
​
​
    //定义一个变量,记录当前展示图片的路径
    String path="..\\PuzzleGame\\image\\animal\\animal3\\";
​
​
    //定义一个二维数组,存储正确的数据
    int[][] win={
        {1,2,3,4},
        {5,6,7,8},
        {9,10,11,12},
        {13,14,15,0},
    };
​
​
    //定义变量用来统计步数,要定义为全局变量,因为如果是局部变量,方法结束就会消失
    int step=0;
​
​
    public GameJFrame(){
        //初始化界面
        initJFrame();
​
        //初始化菜单
        initJMenuBar();
​
        //初始化数据(打乱)
        initData();
​
​
        //初始化图片(根据打乱之后的结果去加载图片)
        initImage();
​
​
​
        //参数为true显示界面,为false隐藏界面-->建议写最后
        this.setVisible(true);//gameJframe.show();也可以显示界面
    }
​
​
    //初始化数据(打乱)的方法
    private void initData() {
        //1.定义一个一维数组
        int[] tempArr={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
        //2.打乱数组中数据的顺序
        Random r=new Random();
        for (int i = 0; i < tempArr.length; i++) {
            //a.随机生成一个索引
            int index=r.nextInt(tempArr.length-1);
            //b.与i索引上的元素交换位置
            int temp=tempArr[i];
            tempArr[i]=tempArr[index];
            tempArr[index]=temp;
        }
        //3.给二维数组添加数据
        /*遍历一维数组tempArr得到每一个元素,把每一个元素依次添加到二维数组当中 */
        for (int i = 0; i < tempArr.length; i++) {
            if(tempArr[i]==0){
                //为0时对坐标进行改变
                x=i/4;
                y=i%4;
            } else {
                //不为0时添加到二维数组中
                data[i/4][i%4]=tempArr[i];
            }
​
        }
    }
​
​
    //初始化图片的方法
     //初始化数据(打乱)后,在添加图片时,就需要根据二维数组中管理的数据进行添加图片
    private void initImage() {
        //需要先清空原本已经出现的所有图片
        this.getContentPane().removeAll();//之后重新加载
​
​
        if(victory()){
            //走if说明方法victory返回true,即胜利
            //需要显示胜利的图标
            JLabel winJLabel=new JLabel(new ImageIcon("D:\\Java\\PuzzleGame\\image\\win.png"));
            //设置图标
            winJLabel.setBounds(203,283,197,73);
            this.getContentPane().add(winJLabel);
        }
​
​
        //步数
        JLabel stepCount=new JLabel("步数;"+step);
        stepCount.setBounds(50,30,100,20);
        this.getContentPane().add(stepCount);
​
​
​
        /* 路径分为两种:
            1.绝对路径:一定是从盘符开始的,如D:\Java
            2.相对路径:相对路径是相对当前项目而言的,不是从盘符开始的.
                如aaa\\bbb,就是在当前项目下去找aaa文件夹,里面再找bbb文件夹
         */
​
​
        /* 细节:
            先加载的图片在上方,后加载的图片在下方。
            本例中背景图片在下方,应该之后加载
         */
​
        //外循环控制x,内循环控制y----嵌套循环(先看内循环,再看外循环)
        //外循环 --- 把内循环重复执行了四次
        for (int i = 0; i < 4; i++) {
            //内循环 --- 表示在一行添加4张图片
            for (int j = 0; j < 4; j++) {
                //获取当前要加载的图片的序号
                int number=data[i][j];
                //创建一个图片ImageIcon的对象,注:number是整型,要单独写出来
                ImageIcon icon=new ImageIcon(path+number+".jpg");/*这是相对路径,PuzzleGame前有个..\\(..//也行)  */
                //创建一个JLabel的对象(管理容器),本例中没有第16张图片,但也会执行new JLabel,只不过加载了个空白
                JLabel jLabel=new JLabel(icon);//放入icon,代表管理icon
                //指定图片位置(必须是在"把管理容器添加到界面中"前)
                /* 83和134是为了把内容移动到中央偏下(这个数值需要自己测试) */
                jLabel.setBounds(105*j+83,105*i+134,105,105);//第一个参数为横坐标,第二个参数为纵坐标,第三个参数为宽(像素),第四个参数为高(像素)
                //给图片添加边框(new BevelBorder(0)代表让图片凸起来,0是RAISED;new BevelBorder(1)代表让图片凹下去,1是LOWERED)
                jLabel.setBorder(new BevelBorder(BevelBorder.LOWERED));//也可以是jLabel.setBorder(new BevelBorder(1));
                //把管理容器添加到界面中
                this.getContentPane().add(jLabel);//getContentPane()用来获取里面隐藏的容器
            }
        }
​
​
        //添加背景图片
        ImageIcon bg=new ImageIcon("..\\PuzzleGame\\image\\background.png");/*这是相对路径,PuzzleGame前有个..\\  */
        //创建一个JLabel的对象(管理容器)
        JLabel background=new JLabel(bg);
        //设置JLabel的对象(管理容器)的位置和宽高-->数值需要自己一点一点测试
        background.setBounds(40,40,508,560);
        //把背景图片(管理容器)添加到界面中
        this.getContentPane().add(background);//getContentPane()用来获取里面隐藏的容器
​
​
​
        //刷新一下界面
        this.getContentPane().repaint();
​
    }
​
​
​
​
​
    //初始化菜单的方法
    private void initJMenuBar() {
        //初始化菜单
        //1.创建整个的菜单对象
        JMenuBar jMenuBar=new JMenuBar();
        //2.创建菜单上面的两个选项的对象(功能  关于我们)
        JMenu functionJMenu=new JMenu("功能");
        JMenu aboutJMenu=new JMenu("关于我们");
        //3.创建选项下面的条目对象
        //功能
        JMenuItem replayItem=new JMenuItem("重新游戏");
        JMenuItem reLoginItem=new JMenuItem("重新登录");
        JMenuItem closeItem=new JMenuItem("关闭游戏");
        //关于我们
        JMenuItem accountItem=new JMenuItem("公众号");
​
​
        //将每一个选项下面的条目添加到选项当中
        //功能
        functionJMenu.add(replayItem);
        functionJMenu.add(reLoginItem);
        functionJMenu.add(closeItem);
        //关于我们
        aboutJMenu.add(accountItem);
​
​
        //将菜单里面的两个选项添加到菜单中
        jMenuBar.add(functionJMenu);
        jMenuBar.add(aboutJMenu);
​
​
        //给整个界面设置菜单
        this.setJMenuBar(jMenuBar);
    }
​
​
​
    //初始化界面的方法
    private void initJFrame() {
        //设置界面的宽高(单位:像素)-->大小根据内容定
        this.setSize(603,680);//第一个参数为宽,第二个参数为高
        //设置界面的标题
        this.setTitle("拼图单机版 v1.0");
        //设置界面置顶
        this.setAlwaysOnTop(true);
        //设置界面居中
        this.setLocationRelativeTo(null);
        //设置关闭模式-->不设置的话最终运行后再关闭界面,但程序不停止
        //this.setDefaultCloseOperation(3);//3代表一种关闭规则,可通过看源码了解
        //还可以this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        //对于关闭模式,必须每个界面都设置一遍才生效。关闭模式2是关了最后一个才结束,关闭模式3是关一个就结束一个
        //本项目这几个界面不会同时出现,因此用关闭模式3即可
        this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        //取消默认的居中放置,只有取消了才会按照XY轴的形式添加组件
        this.setLayout(null);
        //给整个界面添加键盘监听事件
        this.addKeyListener(this);
    }
​
    @Override
    public void keyTyped(KeyEvent e) {
​
    }
​
    //按下不松时,调用这个方法
    @Override
    public void keyPressed(KeyEvent e) { //按下不松:可用于写快捷键
        int code = e.getKeyCode();
        //本例以A为快捷键,A对应编号为65
        if(code==65){
            //把界面中所有图片全部删除
            this.getContentPane().removeAll();
            //加载第一张完整的图片
            ImageIcon icon=new ImageIcon(path+"all.jpg");
            JLabel all=new JLabel(icon);
            all.setBounds(83,134,420,420);//前两个为坐标,因题而异;后两个为宽高(像素),和图片一致即可
            this.getContentPane().add(all);
            //加载背景图片
            ImageIcon bg=new ImageIcon("..\\PuzzleGame\\image\\background.png");/*这是相对路径,PuzzleGame前有个..\\  */
            JLabel background=new JLabel(bg);
            background.setBounds(40,40,508,560);
            this.getContentPane().add(background);//getContentPane()用来获取里面隐藏的容器
            //刷新界面
            this.getContentPane().repaint();
        }
    }
​
    @Override
    public void keyReleased(KeyEvent e) {
        //需要先判断游戏是否胜利,如果胜利,此方法需要直接结束,不能再执行下面的移动代码了
        if(victory()){
            //走if说明方法victory返回true,即胜利-->结束方法
            return;
        }
​
​
        //对上,下,左,右进行判断(各对应一个编号-->不需要记,用的时候打印一下即可知道编号是几)
        //左:37;上:38;右:39;下:40
        int code=e.getKeyCode();
        if(code==37){
            System.out.println("向左移动");
            /* 逻辑:
                把空白方块右方的数字往左移动
             */
            if(y==3){
                //表示空白方块已经在最右方了,他的右方没有图片再能向左移动了-->直接结束方法即可
                return;
            }
​
            //把空白方块右方的数字赋值给空白方块
            data[x][y]=data[x][y+1]; //x,y表示空白方块
            data[x][y+1]=0;//0代表第0张图片即空白图片
            y++;//空白方块此时向右移动了,y也就自增
            //每移动一次,计数器就自增一次
            step++;
            //调用方法按照最新的数字加载图片
            initImage();
​
        } else if (code==38) {
            System.out.println("向上移动");
            /* 逻辑:
                把空白方块下方的数字往上移动
                x,y表示空白方块
                x+1,y表示空白方块下方的数字
             */
            /* 特例:如果空白块在最下方如一开始x=3,y=0
                  此时x+1就越界了
                因此要做越界判断
             */
            if(x==3){
                //表示空白方块已经在最下方了,他的下方没有图片再能向上移动了-->直接结束方法即可
                return;
            }
​
            //把空白方块下方的数字赋值给空白方块
            data[x][y]=data[x+1][y];
            data[x+1][y]=0;//0代表第0张图片即空白图片
            x++;//空白方块此时向下移动了,x也就自增
            //每移动一次,计数器就自增一次
            step++;
            //调用方法按照最新的数字加载图片
            initImage();
​
        } else if (code==39) {
            System.out.println("向右移动");
            /* 逻辑:
                把空白方块左方的数字往右移动
             */
            if(y==0){
                //表示空白方块已经在最左方了,他的左方没有图片再能向右移动了-->直接结束方法即可
                return;
            }
​
            //把空白方块左方的数字赋值给空白方块
            data[x][y]=data[x][y-1]; //x,y表示空白方块
            data[x][y-1]=0;//0代表第0张图片即空白图片
            y--;//空白方块此时向左移动了,y也就自减
            //每移动一次,计数器就自增一次
            step++;
            //调用方法按照最新的数字加载图片
            initImage();
​
        } else if (code==40) {
            System.out.println("向下移动");
            /* 逻辑:
                把空白方块上方的数字往下移动
             */
            if(x==0){
                //表示空白方块已经在最上方了,他的上方没有图片再能向下移动了-->直接结束方法即可
                return;
            }
​
            //把空白方块上方的数字赋值给空白方块
            data[x][y]=data[x-1][y];
            data[x-1][y]=0;//0代表第0张图片即空白图片
            x--;//空白方块此时向上移动了,x也就自减
            //每移动一次,计数器就自增一次
            step++;
            //调用方法按照最新的数字加载图片
            initImage();
​
        } else if (code==65) {
            //此时是松开了,加载图片即可
            initImage();
​
        } else if (code==87) { //本例以W为作弊码,W对应编码为87
            //直接把二维数组里的数据变为最终效果即可
            data=new int[][]{  //直接添加元素,1到15和0是图片编号-->元素内容就是最终效果
                    {1,2,3,4},
                    {5,6,7,8},
                    {9,10,11,12},
                    {13,14,15,0},
            };
            //重新加载图片
            initImage();
        }
    }
​
​
​
    //定义一个方法,用来判断胜利
    /* 判断data数组中的数据是否跟win数组中相同
          如果全部相同,返回true,否则返回false
     */
    public boolean victory(){
        for (int i = 0; i < data.length; i++) {
            //i:依次表示二维数组data里面的索引
            //data[i]:依次表示每一个一维数组
            for (int j = 0; j < data[i].length; j++) {
                if (data[i][j]!=win[i][j]) {
                    //只要有一个数据不一样就没赢,返回false
                    return false;
                }
            }
        }
        //此时还没有结束方法,循环结束表示数组遍历比较完毕,全部一样返回true
        return true;
    }
}

​

2.测试类:

import com.itheima.ui.GameJFrame;
import com.itheima.ui.LoginJFrame;
import com.itheima.ui.RegisterJFrame;
​
public class App {
    public static void main(String[] args) {
        //表示程序的启动入口
​
        //如果我们想要开启一个界面,就创建谁的对象就可以了
        //new LoginJFrame();//调用了登录界面的空参构造
​
        //new RegisterJFrame();
​
        new GameJFrame();
    }
}

​
​

3.运行结果:

移动几步后:


三.重新开始:

四.关闭游戏:

五.关于我们:

六.代码实现:

1.代码:

package com.itheima.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 GameJFrame extends JFrame implements KeyListener, ActionListener {
    /* 注意一定要继承JFrame类
      因为此时要创建一个窗体,而JFrame就是一个界面,窗体,
      继承JFrame的子类也表示界面,窗体
    */
    //规定:GameJFrame这个界面表示的就是游戏的主界面
    //以后跟游戏相关的所有逻辑都写在这个类中
​
​
    //创建一个二维数组
    //目的:用来管理数据
    //加载图片的时候,会根据二维数组中的数据进行加载
    int[][] data=new int[4][4];
​
    //记录空白方块在二维数组中的位置-->一开始设为0
    int x=0;
    int y=0;
​
​
    //定义一个变量,记录当前展示图片的路径
    String path="..\\PuzzleGame\\image\\animal\\animal3\\";
​
​
    //定义一个二维数组,存储正确的数据
    int[][] win={
        {1,2,3,4},
        {5,6,7,8},
        {9,10,11,12},
        {13,14,15,0},
    };
​
​
    //定义变量用来统计步数,要定义为全局变量,因为如果是局部变量,方法结束就会消失
    int step=0;
​
​
​
    //创建选项下面的条目对象
    //功能
    JMenuItem replayItem=new JMenuItem("重新游戏");
    JMenuItem reLoginItem=new JMenuItem("重新登录");
    JMenuItem closeItem=new JMenuItem("关闭游戏");
    //关于我们
    JMenuItem accountItem=new JMenuItem("公众号");
​
​
    public GameJFrame(){
        //初始化界面
        initJFrame();
​
        //初始化菜单
        initJMenuBar();
​
        //初始化数据(打乱)
        initData();
​
​
        //初始化图片(根据打乱之后的结果去加载图片)
        initImage();
​
​
​
        //参数为true显示界面,为false隐藏界面-->建议写最后
        this.setVisible(true);//gameJframe.show();也可以显示界面
    }
​
​
    //初始化数据(打乱)的方法
    private void initData() {
        //1.定义一个一维数组
        int[] tempArr={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
        //2.打乱数组中数据的顺序
        Random r=new Random();
        for (int i = 0; i < tempArr.length; i++) {
            //a.随机生成一个索引
            int index=r.nextInt(tempArr.length-1);
            //b.与i索引上的元素交换位置
            int temp=tempArr[i];
            tempArr[i]=tempArr[index];
            tempArr[index]=temp;
        }
        //3.给二维数组添加数据
        /*遍历一维数组tempArr得到每一个元素,把每一个元素依次添加到二维数组当中 */
        for (int i = 0; i < tempArr.length; i++) {
            if(tempArr[i]==0){
                //为0时就是空白格,要记录位置
                x=i/4;
                y=i%4;
            }
            //不用if-else,因为如果用了else,在重新游戏时遇到0就会不添加到二维数组中,就是之前的图片
            data[i/4][i%4]=tempArr[i];//此时不管是否为0,都添加到二维数组中
        }
    }
​
​
    //初始化图片的方法
     //初始化数据(打乱)后,在添加图片时,就需要根据二维数组中管理的数据进行添加图片
    private void initImage() {
        //需要先清空原本已经出现的所有图片
        this.getContentPane().removeAll();//之后重新加载
​
​
        if(victory()){
            //走if说明方法victory返回true,即胜利
            //需要显示胜利的图标
            JLabel winJLabel=new JLabel(new ImageIcon("D:\\Java\\PuzzleGame\\image\\win.png"));
            //设置图标
            winJLabel.setBounds(203,283,197,73);
            this.getContentPane().add(winJLabel);
        }
​
​
        //步数
        JLabel stepCount=new JLabel("步数;"+step);
        stepCount.setBounds(50,30,100,20);
        this.getContentPane().add(stepCount);
​
​
​
        /* 路径分为两种:
            1.绝对路径:一定是从盘符开始的,如D:\Java
            2.相对路径:相对路径是相对当前项目而言的,不是从盘符开始的.
                如aaa\\bbb,就是在当前项目下去找aaa文件夹,里面再找bbb文件夹
         */
​
​
        /* 细节:
            先加载的图片在上方,后加载的图片在下方。
            本例中背景图片在下方,应该之后加载
         */
​
        //外循环控制x,内循环控制y----嵌套循环(先看内循环,再看外循环)
        //外循环 --- 把内循环重复执行了四次
        for (int i = 0; i < 4; i++) {
            //内循环 --- 表示在一行添加4张图片
            for (int j = 0; j < 4; j++) {
                //获取当前要加载的图片的序号
                int number=data[i][j];
                //创建一个图片ImageIcon的对象,注:number是整型,要单独写出来
                ImageIcon icon=new ImageIcon(path+number+".jpg");/*这是相对路径,PuzzleGame前有个..\\(..//也行)  */
                //创建一个JLabel的对象(管理容器),本例中没有第16张图片,但也会执行new JLabel,只不过加载了个空白
                JLabel jLabel=new JLabel(icon);//放入icon,代表管理icon
                //指定图片位置(必须是在"把管理容器添加到界面中"前)
                /* 83和134是为了把内容移动到中央偏下(这个数值需要自己测试) */
                jLabel.setBounds(105*j+83,105*i+134,105,105);//第一个参数为横坐标,第二个参数为纵坐标,第三个参数为宽(像素),第四个参数为高(像素)
                //给图片添加边框(new BevelBorder(0)代表让图片凸起来,0是RAISED;new BevelBorder(1)代表让图片凹下去,1是LOWERED)
                jLabel.setBorder(new BevelBorder(BevelBorder.LOWERED));//也可以是jLabel.setBorder(new BevelBorder(1));
                //把管理容器添加到界面中
                this.getContentPane().add(jLabel);//getContentPane()用来获取里面隐藏的容器
            }
        }
​
​
        //添加背景图片
        ImageIcon bg=new ImageIcon("..\\PuzzleGame\\image\\background.png");/*这是相对路径,PuzzleGame前有个..\\  */
        //创建一个JLabel的对象(管理容器)
        JLabel background=new JLabel(bg);
        //设置JLabel的对象(管理容器)的位置和宽高-->数值需要自己一点一点测试
        background.setBounds(40,40,508,560);
        //把背景图片(管理容器)添加到界面中
        this.getContentPane().add(background);//getContentPane()用来获取里面隐藏的容器
​
​
​
        //刷新一下界面
        this.getContentPane().repaint();
​
    }
​
​
​
​
​
    //初始化菜单的方法
    private void initJMenuBar() {
        //初始化菜单
        //1.创建整个的菜单对象
        JMenuBar jMenuBar=new JMenuBar();
        //2.创建菜单上面的两个选项的对象(功能  关于我们)
        JMenu functionJMenu=new JMenu("功能");
        JMenu aboutJMenu=new JMenu("关于我们");
​
​
        //将每一个选项下面的条目添加到选项当中
        //功能
        functionJMenu.add(replayItem);
        functionJMenu.add(reLoginItem);
        functionJMenu.add(closeItem);
        //关于我们
        aboutJMenu.add(accountItem);
​
        //给条目绑定事件
        replayItem.addActionListener(this);
        reLoginItem.addActionListener(this);
        closeItem.addActionListener(this);
        accountItem.addActionListener(this);
​
​
        //将菜单里面的两个选项添加到菜单中
        jMenuBar.add(functionJMenu);
        jMenuBar.add(aboutJMenu);
​
​
        //给整个界面设置菜单
        this.setJMenuBar(jMenuBar);
    }
​
​
​
    //初始化界面的方法
    private void initJFrame() {
        //设置界面的宽高(单位:像素)-->大小根据内容定
        this.setSize(603,680);//第一个参数为宽,第二个参数为高
        //设置界面的标题
        this.setTitle("拼图单机版 v1.0");
        //设置界面置顶
        this.setAlwaysOnTop(true);
        //设置界面居中
        this.setLocationRelativeTo(null);
        //设置关闭模式-->不设置的话最终运行后再关闭界面,但程序不停止
        //this.setDefaultCloseOperation(3);//3代表一种关闭规则,可通过看源码了解
        //还可以this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        //对于关闭模式,必须每个界面都设置一遍才生效。关闭模式2是关了最后一个才结束,关闭模式3是关一个就结束一个
        //本项目这几个界面不会同时出现,因此用关闭模式3即可
        this.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        //取消默认的居中放置,只有取消了才会按照XY轴的形式添加组件
        this.setLayout(null);
        //给整个界面添加键盘监听事件
        this.addKeyListener(this);
    }
​
    @Override
    public void keyTyped(KeyEvent e) {
​
    }
​
    //按下不松时,调用这个方法
    @Override
    public void keyPressed(KeyEvent e) { //按下不松:可用于写快捷键
        int code = e.getKeyCode();
        //本例以A为快捷键,A对应编号为65
        if(code==65){
            //把界面中所有图片全部删除
            this.getContentPane().removeAll();
            //加载第一张完整的图片
            ImageIcon icon=new ImageIcon(path+"all.jpg");
            JLabel all=new JLabel(icon);
            all.setBounds(83,134,420,420);//前两个为坐标,因题而异;后两个为宽高(像素),和图片一致即可
            this.getContentPane().add(all);
            //加载背景图片
            ImageIcon bg=new ImageIcon("..\\PuzzleGame\\image\\background.png");/*这是相对路径,PuzzleGame前有个..\\  */
            JLabel background=new JLabel(bg);
            background.setBounds(40,40,508,560);
            this.getContentPane().add(background);//getContentPane()用来获取里面隐藏的容器
            //刷新界面
            this.getContentPane().repaint();
        }
    }
​
    @Override
    public void keyReleased(KeyEvent e) {
        //需要先判断游戏是否胜利,如果胜利,此方法需要直接结束,不能再执行下面的移动代码了
        if(victory()){
            //走if说明方法victory返回true,即胜利-->结束方法
            return;
        }
​
​
        //对上,下,左,右进行判断(各对应一个编号-->不需要记,用的时候打印一下即可知道编号是几)
        //左:37;上:38;右:39;下:40
        int code=e.getKeyCode();
        if(code==37){
            System.out.println("向左移动");
            /* 逻辑:
                把空白方块右方的数字往左移动
             */
            if(y==3){
                //表示空白方块已经在最右方了,他的右方没有图片再能向左移动了-->直接结束方法即可
                return;
            }
​
            //把空白方块右方的数字赋值给空白方块
            data[x][y]=data[x][y+1]; //x,y表示空白方块
            data[x][y+1]=0;//0代表第0张图片即空白图片
            y++;//空白方块此时向右移动了,y也就自增
            //每移动一次,计数器就自增一次
            step++;
            //调用方法按照最新的数字加载图片
            initImage();
​
        } else if (code==38) {
            System.out.println("向上移动");
            /* 逻辑:
                把空白方块下方的数字往上移动
                x,y表示空白方块
                x+1,y表示空白方块下方的数字
             */
            /* 特例:如果空白块在最下方如一开始x=3,y=0
                  此时x+1就越界了
                因此要做越界判断
             */
            if(x==3){
                //表示空白方块已经在最下方了,他的下方没有图片再能向上移动了-->直接结束方法即可
                return;
            }
​
            //把空白方块下方的数字赋值给空白方块
            data[x][y]=data[x+1][y];
            data[x+1][y]=0;//0代表第0张图片即空白图片
            x++;//空白方块此时向下移动了,x也就自增
            //每移动一次,计数器就自增一次
            step++;
            //调用方法按照最新的数字加载图片
            initImage();
​
        } else if (code==39) {
            System.out.println("向右移动");
            /* 逻辑:
                把空白方块左方的数字往右移动
             */
            if(y==0){
                //表示空白方块已经在最左方了,他的左方没有图片再能向右移动了-->直接结束方法即可
                return;
            }
​
            //把空白方块左方的数字赋值给空白方块
            data[x][y]=data[x][y-1]; //x,y表示空白方块
            data[x][y-1]=0;//0代表第0张图片即空白图片
            y--;//空白方块此时向左移动了,y也就自减
            //每移动一次,计数器就自增一次
            step++;
            //调用方法按照最新的数字加载图片
            initImage();
​
        } else if (code==40) {
            System.out.println("向下移动");
            /* 逻辑:
                把空白方块上方的数字往下移动
             */
            if(x==0){
                //表示空白方块已经在最上方了,他的上方没有图片再能向下移动了-->直接结束方法即可
                return;
            }
​
            //把空白方块上方的数字赋值给空白方块
            data[x][y]=data[x-1][y];
            data[x-1][y]=0;//0代表第0张图片即空白图片
            x--;//空白方块此时向上移动了,x也就自减
            //每移动一次,计数器就自增一次
            step++;
            //调用方法按照最新的数字加载图片
            initImage();
​
        } else if (code==65) {
            //此时是松开了,加载图片即可
            initImage();
​
        } else if (code==87) { //本例以W为作弊码,W对应编码为87
            //直接把二维数组里的数据变为最终效果即可
            data=new int[][]{  //直接添加元素,1到15和0是图片编号-->元素内容就是最终效果
                    {1,2,3,4},
                    {5,6,7,8},
                    {9,10,11,12},
                    {13,14,15,0},
            };
            //重新加载图片
            initImage();
        }
    }
​
​
​
    //定义一个方法,用来判断胜利
    /* 判断data数组中的数据是否跟win数组中相同
          如果全部相同,返回true,否则返回false
     */
    public boolean victory(){
        for (int i = 0; i < data.length; i++) {
            //i:依次表示二维数组data里面的索引
            //data[i]:依次表示每一个一维数组
            for (int j = 0; j < data[i].length; j++) {
                if (data[i][j]!=win[i][j]) {
                    //只要有一个数据不一样就没赢,返回false
                    return false;
                }
            }
        }
        //此时还没有结束方法,循环结束表示数组遍历比较完毕,全部一样返回true
        return true;
    }
​
    @Override
    public void actionPerformed(ActionEvent e) {
        //获取当前被点击的条目对象
        Object obj = e.getSource();
        //判断
        if(obj==replayItem){
            System.out.println("重新游戏");
​
            //计步器清零-->要在重新加载图片前清零,不然显示的步数就是未清零的步数
            step=0;
            //再次打乱二维数组中的数据
            initData();
            //重新加载图片
            initImage();
​
​
        } else if (obj==reLoginItem) {
            System.out.println("重新登录");
            //关闭当前的游戏界面
            this.setVisible(false);
            //打开登录界面-->创建登录界面对象
            new LoginJFrame();
        } else if (obj==closeItem) {
            System.out.println("关闭游戏");
            //直接关闭虚拟机即可
            System.exit(0);
        } else if (obj==accountItem) {
            System.out.println("公众号");
​
            //创建一个弹框对象
            JDialog jDialog=new JDialog();
            //创建一个管理图片的容器对象JLabel
            JLabel jLabel=new JLabel(new ImageIcon("..\\PuzzleGame\\image\\about.png"));
            //设置位置和宽高,这里的位置相对于弹框而言的
            jLabel.setBounds(0,0,258,258);
            //把图片添加到弹框当中
            jDialog.getContentPane().add(jLabel);
            //给弹框设置大小
            jDialog.setSize(344,344);
            //让弹框置顶
            jDialog.setAlwaysOnTop(true);
            //让弹框居中
            jDialog.setLocationRelativeTo(null);
            //弹框不关闭则无法操作下面的界面
            jDialog.setModal(true);
            //让弹框显示出来
            jDialog.setVisible(true);
        }
    }
}

​

2.测试类:

import com.itheima.ui.GameJFrame;
import com.itheima.ui.LoginJFrame;
import com.itheima.ui.RegisterJFrame;
​
public class App {
    public static void main(String[] args) {
        //表示程序的启动入口
​
        //如果我们想要开启一个界面,就创建谁的对象就可以了
        //new LoginJFrame();//调用了登录界面的空参构造
​
        //new RegisterJFrame();
​
        new GameJFrame();
    }
}

​
​

3.运行结果:


华为OD机试真题 2026.11.4. 新系统 【河流水系流量评估】 华为OD机试真题 新系统【河流水系流量评估】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为OD机试真题 【河流水系流量评估】多语言题解。 阅读详情

相关推荐

RAG开源框架深度评测:从RAGFlow到Dify的实战选型指南

本文深度评测了主流RAG开源框架(RAGFlow、Dify、WeKnora)的核心能力与实战表现,提供企业级选型指南。重点对比了文档解析、检索性能和生产部署等关键指标,揭示RAGFlow在复杂文档处理、Dify在低代码开发、WeKnora在腾讯生态集成的差异化优势,并给出技术选型决策树与成本优化方案。

weixin_29294597的博客 827

day14--多态 接口 抽象类 内部类 Day16-项目-拼图小游戏 组件 断点调试&快捷键

阶段项目-01-项目介绍和界面搭建 阶段项目-02-界面设置和菜单搭建 阶段项目-03-添加图片 阶段项目-04-打乱图片 阶段项目-05-事件 阶段项目-06-美化界面 阶段项目-07-移动图片 阶段项目-08-查看完整图片、作弊码和判断胜利 阶段项目-09-计步和菜单业务实现 阶段项目-10-阶段项目课后练习思路分析 阶段项目-11-游戏打包成exe安装包.........

我亦无他,唯手熟尔 713

ansys命令流力磁耦合仿真

文章目录前言一、力磁耦合仿真流程二、静力学仿真三、力磁耦合模型构建四、静磁学仿真总结 前言 一、力磁耦合仿真流程 力磁耦合有限元仿真流程中,首先进行静力学仿真,经过前处理模块、分析计算模块和后处理模块提取三维模型单元应力,然后根据力磁耦合方程中,通过matlab将单元应力导入到磁导率与应力的关系式中,得到单元的相对磁导率。最后采用载荷传递耦合方式,进行静磁学模拟仿真,分析计算得到该载荷下试件的磁场分布。,如下图所示为力磁耦合仿真流程图: 二、静力学仿真 下图为中心预制条形缺陷的Q235钢板试..

wsfwsf128的博客 2662

STM32上MotionFX传感器融合库实战:从原理到姿态解算调通

传感器融合是惯性测量单元(IMU)在可穿戴设备、机器人、无人机等场景中输出稳定姿态的关键技术。加速度计低频响应准但易受运动加速度干扰,陀螺仪高频响应好但存在积分漂移,磁力计提供绝对航向却易受环境干扰,三者误差互补使其成为姿态解算的基础。姿态航向参考系统(AHRS)通过融合算法动态调整各传感器权重,实现高精度横滚、俯仰与航向角输出。ST推出的MotionFX传感器融合库以动态自适应滤波为核心,在STM32平台上提供完整的6轴/9轴融合方案,涵盖四元数、欧拉角转换及磁力计校准等工程实践。本文从传感器原理切入,系

weixin_30655569的博客 415

STM32F407 FreeRTOS+LVGL移植实战:从驱动适配到性能优化

本文详细介绍了在STM32F407开发板上移植FreeRTOS和LVGL的实战经验,涵盖硬件准备、驱动适配、性能优化等关键步骤。通过TFTLCD屏幕驱动优化、DMA加速和任务优先级调整,显著提升界面流畅度,为嵌入式GUI开发提供实用解决方案。

weixin_29190651的博客 632

第一篇:常见CAN错误帧及其排查方向

在本文中主要阐述各类错误帧的形成原因以及在使用同星硬件设备出现错误帧时,应当 如何进行排查并消除错误帧。在 CAN 总线通信中,通常遇见的错误帧主要为以下五种,其 分别是:bit error、ACK error、stuff error、CRC error 以及 form error。

u013089100的博客 1742

InnoDB 内存结构总览:Change Buffer、AHI 与 Log Buffer

本文系统梳理InnoDB内存结构中的ChangeBuffer、Adaptive Hash Index(AHI)与LogBuffer。ChangeBuffer缓存二级索引页不在内存时的变更,减少随机IO,仅在页加载时合并;聚簇索引修改立即执行,因数据本身不可延后。AHI基于高频等值查询自动构建哈希索引,加速定位,仅支持等值操作。LogBuffer为redo log内存缓冲,配合磁盘日志实现崩溃恢复。三者均服务于性能优化,其中ChangeBuffer与AHI共享BufferPool空间,而LogBuffer独立

bksczm的博客 282

通达信大单资金实时监控与播报插件:股票分析的实用利器——大红球

本文介绍了一款专为通达信软件设计的大单资金实时监控与播报插件,该插件以其傻瓜式操作和入门级特性在股民中广受欢迎。插件核心功能包括与通达信版面的无缝联动、大单金额范围的自定义设置、资金流动的可视化展示以及智能声音播报等。其技术架构基于本地网页形式,自带分时图显示功能,数据实时更新且无需L2账户,承诺永久免费升级。插件通过红绿球的大小和颜色直观展示资金规模与买卖方向,顶端全局统计功能提供宏观资金流动分析。尽管插件数据具有较高参考价值,但投资者仍需结合其他因素进行综合判断。插件界面可自适应调整,提供个性化使用体验

guleba的博客 6592

Spring AI + GPT-5.5 + Ollama 配置教程:Base URL、超时与流式调用

由于不同 Spring AI、Spring Boot 小版本之间仍可能存在差异,创建项目时应检查 Spring Initializr、Spring AI BOM 和官方兼容矩阵,不要直接复制旧教程中的 Starter 名称和版本号。多人项目最好隔离不同客户端,并在脱敏前提下记录最终 URL、状态码、请求耗时、请求 ID 和模型 ID。Spring AI 接入模型 API,至少要同时确认 Starter、Base URL、请求路径、API Key、模型 ID、连接超时、读取超时和流式客户端这 8 项。

2601_95893075的博客 232

一文吃透 SAP IDoc 状态码:从 TEDS1 到 WE02 排障,再到 ABAP 自动化监控

在 SAP 集成场景里,IDoc 往往是最常见、也最容易被低估的一条生命线:采购订单、发票、主数据、库存移动……只要系统之间要稳定地交换结构化业务数据,IDoc 就经常登场。真正让人头疼的,并不是 IDoc 本身,而是它在链路里到底走到了哪一步、卡在了哪里、该找谁背锅。这篇文章把常用的 IDoc 状态码按出站、入站拆开讲清楚,并且把每个状态对应的真实世界故障现象、排查入口、以及可以落地的 ABAP 监控代码串起来,让你看到状态码就能想到动作。

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 405

[VT虚拟化驱动]利用EPT实现无痕HOOK

本章利用讲EPT无痕HOOK

吾无法无天的博客 1万+

V8 机制、手写核心函数与 TypeScript 类型体操

或固定初始化(如数组先定长、对象先给全字段)保持类型与形状稳定;这些都比"换框架"更基础。只能处理纯数据(丢函数、Date、RegExp、循环引用报错)。生产级深拷贝要处理:原始值短路、循环引用、V8(Chrome / Node 的 JS 引擎)不是"解释一行跑一行"那么简单,它用。图:V8 执行流程——解释执行 + 热点优化,优化失败会 Deopt 回退。JS 内存自动管理,但"自动"不等于"不会漏"。是"固定间隔最多执行一次"。手写 Promise 是"懂异步"的试金石。是"停下后才执行最后一次",

qq_38468358的博客 346

Android 系统低内存lowmemorykiller关于阈值修改 minfreeAbsKbytes优化

在系统rom定制化开发中,在对于运行内存ram 1G和2G的设备中,对于内存使用高峰期时,minfreeAbsKbytes优化很关键 调整合理的minfree,让内存合理的使用很关键,接下来分析相关功能的实现

安卓兼职framework和app工程师的博客 524

Java EE 进阶:8.MyBatis 操作数据库(入门)(第一弹)

Java EE 进阶:8.MyBatis 操作数据库(入门)(第一弹)

要努力去发光,而不是被照亮~ 126

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

WINCC7.3破解文件

WINCC7.3硬件够破解文件,有需要的下载!WINCC7.3硬件够破解文件,有需要的下载!

Spring Boot 数据源与 MyBatis(-Plus) 自定义配置详解

本文详解 Spring Boot 中单数据源环境下 HikariCP 与 MyBatis-Plus 的自定义配置。通过手动声明 SqlSessionFactory,实现下划线转驼峰、日志输出、一级缓存策略等精细化控制。结合 HikariCP 核心参数调优(如最大连接数、连接生命周期),确保高性能与稳定性。适用于仅需访问单一数据库的场景,避免多数据源复杂性,兼顾性能与可维护性。

BADAO_LIUMANG_QIZHI的博客 196

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

LeetCode Hot 100 二叉树专题(二)3 题(Java)

本篇文章共 3 题:543. 二叉树的直径、102. 二叉树的层序遍历、108. 将有序数组转换为二叉搜索树。 每道题都统一按「① 题目分析 & 解题思路 → ② 常见解法(附代码)及讲解 → ③ 知识点总结」的顺序组织;代码使用 Java 编写,节点定义沿用 LeetCode 默认的 TreeNode。

2401_88612756的博客 365

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Decorator

TaskDecorator 是异步场景下"跨线程传递上下文"的标准方案,主要用于传递 MDC、用户信息、数据源路由等上下文数据,核心价值是让异步任务的日志链路不中断、业务上下文不丢失。TaskDecorator 是 Spring 提供的一个钩子接口,用于在异步任务执行前对 Runnable 进行包装,常用于跨线程传递上下文。如果你在项目中遇到具体的异步上下文传递问题,可以把代码贴出来,我帮你给出最合适的 Decorator 方案 👍。✅ 核心能力:在线程切换时,把"父线程的上下文"搬到"子线程"

wlwork66的博客 106

JDK 下载安装:装完 Java 提示「不是内部或外部命令」?环境变量没配

JDK 是写 Java 程序必须装的东西。它包含运行 Java 的虚拟机(JRE)和编译 Java 的编译器(javac)——装了它,能跑、.java文件能编译成可执行程序。免费,学习和小规模生产都不花钱。

科小怪的博客 631

C++ 多态详解:作用、原理与实战用法

多态是C++面向对象的核心特性,通过虚函数实现运行期动态绑定,使同一接口在不同对象上表现出不同行为。其核心作用包括统一接口、提升可扩展性、降低耦合、支持运行时分派与批量处理。实现依赖虚函数表(vtable)和虚指针(vptr),需注意虚析构函数、对象切片等陷阱。相比编译期多态(模板),运行期多态灵活性高但有调用开销。合理结合两者,可兼顾代码的灵活性与性能,是构建可扩展、低耦合系统的关键。

2402_85743155的博客 382

java框架大全

java框架大全

qq_39326137的博客 183

Java 多态 学习笔记

多态是面向对象编程的重要特性,指同一操作作用于不同对象时产生不同行为。其核心在于“父类引用指向子类对象”,结合方法重写实现运行时动态绑定。多态使代码更灵活、可扩展,如用Person p = new Student();统一处理各类人员注册,无需修改调用逻辑。但多态无法直接调用子类特有方法,需通过instanceof判断并强制转换。编译时看左边(类型),运行时看右边(实际对象),实现解耦与维护便利。

AA_AA114514的博客 359

SecureRandom做随机源,这个Java随机字符串工具类有3个方法

本文分享一款JDK17环境下的Java随机字符串工具类,内置3个开箱即用的方法,全部采用SecureRandom加密安全随机源,不存在弱随机、可预测、取模偏差等问题。工具类支持默认62字符集随机串、纯数字随机串、自定义字符集随机串三种生成模式,支持Unicode码点级别的字符集处理,兼容Emoji等增补字符,边界处理严格,异常信息明确,适合验证码、邀请码、盐值、随机口令、短链码等场景,源码可直接复制使用。

Java后端 | AI智能体应用实战派 | 电脑技能行家 298

Java实现批量线段相交检测(跨积判定+扫描线优化与踩坑复盘)

【代码】Java实现批量线段相交检测(跨积判定+扫描线优化与踩坑复盘)

m0_64476639的博客 326

ubuntu安装hadoop系统

本文详细介绍了基于VMware克隆虚拟机搭建三节点Hadoop分布式集群的全过程:使用同一Ubuntu镜像克隆出master、worker1、worker2三台虚拟机,依次配置主机名、固定IP(如192.168.82.10/11/12)、SSH服务及免密登录,确保节点间网络互通。随后安装Java,下载配置Hadoop并分发至各节点,最终初始化启动集群,验证其存储与计算功能,实现高效的大数据环境部署。

qq_43592674的博客 329

Agent 状态栏在上下文中的具体位置

Agent状态栏以user角色消息形式追加至上下文末尾,而非修改system消息,避免破坏KVCache。此举仅为API技术实现,内容由框架自动生成,非真实用户输入,通过<agent_status>标签标识,确保模型获取最新状态且不影响缓存。该设计遵循“动态信息追加、静态信息固定”原则,实现高效、低延迟的状态传递。

lj1109053360的专栏 72

Mac上装了最新版本的Android Studio,但是没找到jdk的设置选项也没找到jbr的目录

新版Android Studio(2025.3.1+)因三项变更导致常见问题:一、JDK设置移至“DaemonJVM criteria”,新项目默认自动探测或下载JDK;二、JBR目录改名为带版本号的jbr-21等,需进入.app/Contents/查看,或通过About窗口确认运行时;三、命令行缺adb因platform-tools未加入PATH,需在SDK Manager中勾选并配置~/.zshrc中的ANDROID_HOME及PATH。重装或清理残留可解决缺失问题,切勿手动设JAVA_HOME或安装冲

Programming is a highly artistic endeavor 349
上一篇: 06.查看完整图片,作弊码和判断胜利
下一篇: 08.切换游戏图片的业务分析
巧克力拌面
博客等级 码龄3年 1153粉丝 282原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值