自学Java-AI结合GUI开发一个石头迷阵的游戏

news2025/2/23 5:21:51

自学Java-AI结合GUI开发一个石头迷阵的游戏

  • 准备环节
  • 1、创建石头迷阵的界面
  • 2、打乱顺序
  • 3、控制上下左右移动
  • 4、判断是否通关
  • 5、统计移动步骤,重启游戏
  • 6、拓展问题

准备环节

在这里插入图片描述
技术:
1、GUI界面编程
2、二维数组
3、程序流程控制
4、面向对象编程

∙ \bullet 创建一个模块用于开发石头迷阵游戏,模块名称取名为:stone-maze
∙ \bullet 导入项目需要的资源包到src目录下:主要是一些图片文件,在image文件夹下
∙ \bullet 创建项目包:com.itheima.

1、创建石头迷阵的界面

∙ \bullet 定义主界面类,MainFrame继承JFrame
∙ \bullet 初始化窗口大小
∙ \bullet 初始化界面图片
∙ \bullet 初始化界面菜单:系统退出,重启游戏

完成代码如下:

package com.itheima;

import javax.swing.*;
// 自定义窗口类,创建对象,展示一个主窗口。
public class MainFrame extends JFrame {
    // 设置图片位置
    private static final String imagePath = "stone-maze/src/image/";
    // 准备一个数组,用户存储数字色块的行列位置:4行4列
    private int[][] imageData = {
            {1, 2, 3, 4},
            {5, 6, 7, 8},
            {9, 10, 11, 12},
            {13, 14, 15, 0}
    };

    public MainFrame() {
        // 1、调用一个初始化方法:初始化窗口大小等信息。
        initFrame();
        // 2、初始化界面:展示数字色块。
        initImage();
        // 3、初始化系统菜单:点击弹出菜单信息是系统退出,重启游戏。
        initMenu();

        // 设置窗口可见
        this.setVisible(true);
    }

    private void initMenu() {
        JMenuBar menuBar = new JMenuBar(); // 创建一个菜单栏
        JMenu menu = new JMenu("系统");
        JMenuItem exitJi = new JMenuItem("退出");
        menu.add(exitJi); // 添加子菜单
        exitJi.addActionListener(e -> { // 添加点击事件
            // 退出游戏
            dispose();
        });
        JMenuItem restartJi = new JMenuItem("重启");
        menu.add(restartJi);
        restartJi.addActionListener(e -> { // 添加点击事件
            // 重启游戏
        });
        menuBar.add(menu); // 添加到菜单栏中
        this.setJMenuBar(menuBar);
    }

    private void initImage() {
        // 1、展示一个行列矩阵的图片色块依次铺满窗口(4 * 4)
        for (int i = 0; i < imageData.length; i++) { // 遍历行
            for (int j = 0; j < imageData[i].length; j++) { // 遍历列
                // 拿到图片的名称
                String imageName = imageData[i][j] + ".png";
                // 2、创建一个JLabel对象,设置图片给他展示。
                JLabel label = new JLabel();
                // 3、设置图片到label对象中去。
                label.setIcon(new ImageIcon(imagePath + imageName));
                // 4、设置数字色块的位置
                label.setBounds(25 + j * 100, 60 + i * 100, 100, 100);
                // 5、把这个图片展示到窗口中
                this.add(label);
            }
        }

        // 设置窗口的背景图片
        JLabel background = new JLabel(new ImageIcon(imagePath + "background.png"));
        background.setBounds(0, 0, 450, 484);
        this.add(background);
    }

    private void initFrame() {
        // 设置窗口标题
        this.setTitle("石子迷宫 V 1.0");
        // 设置窗口大小
        this.setSize(465, 575);
        // 设置窗口关闭方式
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // 设置窗口的居中显示
        this.setLocationRelativeTo(null);
        // 设置布局方式为绝对位置定位
        this.setLayout(null);
    }
}

2、打乱顺序

∙ \bullet 打乱界面的图片顺序,让游戏具备可玩性:使用方法如下

打乱二维数组中的元素顺序:initRandomArray();

完成代码如下:

private void initRandomArray() {
        // 1、打乱数组
        for (int i = 0; i < imageData.length; i++) { // 遍历行
            for (int j = 0; j < imageData[i].length; j++) { // 遍历列
                // 随机两个行列位置,让这两个位置交换。
                int i1 = (int)(Math.random() * imageData.length);
                int j1 = (int)(Math.random() * imageData.length);

                int i2 = (int)(Math.random() * imageData.length);
                int j2 = (int)(Math.random() * imageData.length);

                int temp = imageData[i1][j1];
                imageData[i1][j1] = imageData[i2][j2];
                imageData[i2][j2] = temp;
            }
        }
    }

3、控制上下左右移动

∙ \bullet 给窗口绑定上下左右按键事件

∙ \bullet 控制位置的交换
–定位当前空白色块的位置。
–根据用户点击的方位确定交换哪个数据,到数组中交换。

∙ \bullet 重新绘制主界面的内容
–让主界面按照二维数组的最新内容刷新界面

public enum Direction {
    UP, DOWN, LEFT, RIGHT;
}

完成代码如下:

package com.itheima;

import javax.swing.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

// 自定义窗口类,创建对象,展示一个主窗口。
public class MainFrame extends JFrame {
    // 设置图片位置
    private static final String imagePath = "stone-maze/src/image/";
    // 准备一个数组,用户存储数字色块的行列位置:4行4列
    private int[][] imageData = {
            {1, 2, 3, 4},
            {5, 6, 7, 8},
            {9, 10, 11, 12},
            {13, 14, 15, 0}
    };

    // 定义两个整数变量记录当前空白色块的位置。
    private int row; // 当前空白色块的行位置
    private int col; // 当前空白色块的列位置

    public MainFrame() {
        // 1、调用一个初始化方法:初始化窗口大小等信息。
        initFrame();
        // 4、打乱数组色块的顺序,再提示图片
        initRandomArray();
        // 2、初始化界面:展示数字色块。
        initImage();
        // 3、初始化系统菜单:点击弹出菜单信息是系统退出,重启游戏。
        initMenu();
        // 5、给当前窗口绑定上下左右按键事件。
        initKeyPressEvent();
        // 设置窗口可见
        this.setVisible(true);
    }

    private void initKeyPressEvent() {
        // 给当前窗口绑定上下左右按键事件。
        this.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                // 判断当前按键的编号
                int keyCode = e.getKeyCode();
                // 判断按键编号,上下左右
                switch (keyCode) {
                    case KeyEvent.VK_UP:
                        switchAndMove(Direction.UP);
                        // 用户按下上键,把图片上移。
                        break;
                    case KeyEvent.VK_DOWN:
                        switchAndMove(Direction.DOWN);
                        // 用户按下下键,把图片下移。
                        break;
                    case KeyEvent.VK_LEFT:
                        switchAndMove(Direction.LEFT);
                        // 用户按下左键,把图片左移。
                        break;
                    case KeyEvent.VK_RIGHT:
                        switchAndMove(Direction.RIGHT);
                        // 用户按下右键,把图片右移。
                        break;
                }
            }
        });
    }

    // 控制数据交换和图片移动
    private void switchAndMove(Direction r) {
        // 判断图片的方向,再控制图片移动。
        switch (r) {
            case UP:
                // 上交换的条件是行必须 < 3,然后才开始交换。
                if (row < imageData.length - 1) {
                    // 当前空白色块位置:rol  col
                    // 需要被交换的位置:row + 1  col
                    int temp = imageData[row][col];
                    imageData[row][col] = imageData[row + 1][col];
                    imageData[row + 1][col] = temp;
                    // 更新当前空白色块的位置。
                    row++;
                }
                break;
            case DOWN:
                if (row > 0) {
                    // 当前空白色块位置:row  col
                    // 需要被交换的位置:row - 1  col
                    int temp = imageData[row][col];
                    imageData[row][col] = imageData[row - 1][col];
                    imageData[row - 1][col] = temp;
                    // 更新当前空白色块的位置。
                    row--;
                }
                break;
            case LEFT:
                // 左交换的条件是列必须 < 3,然后才开始交换。
                if (col < imageData.length - 1) {
                    // 当前空白色块位置:row  col
                    // 需要被交换的位置:row  col + 1
                    int temp = imageData[row][col];
                    imageData[row][col] = imageData[row][col + 1];
                    imageData[row][col + 1] = temp;
                    // 更新当前空白色块的位置。
                    col++;
                }
                break;
            case RIGHT:
                if (col > 0) {
                    int temp = imageData[row][col];
                    imageData[row][col] = imageData[row][col - 1];
                    imageData[row][col - 1] = temp;
                    col--;
                }
                break;
        }
        // 重新刷新界面!!!
        initImage();
    }

    private void initRandomArray() {
        // 1、打乱数组
        for (int i = 0; i < imageData.length; i++) { // 遍历行
            for (int j = 0; j < imageData[i].length; j++) { // 遍历列
                // 随机两个行列位置,让这两个位置交换。
                int i1 = (int)(Math.random() * imageData.length);
                int j1 = (int)(Math.random() * imageData.length);

                int i2 = (int)(Math.random() * imageData.length);
                int j2 = (int)(Math.random() * imageData.length);

                int temp = imageData[i1][j1];
                imageData[i1][j1] = imageData[i2][j2];
                imageData[i2][j2] = temp;
            }
        }

        // 定义空白色块的位置。
        // 去二维数组中遍历每个数据,只要发现这个数据等于0,这个位置就是当前空白色块的位置。
        OUT:
        for (int i = 0; i < imageData.length; i++) { // 遍历行
            for (int j = 0; j < imageData[i].length; j++) { // 遍历列
                if (imageData[i][j] == 0) {
                    // 定位空白色块的位置
                    row = i;
                    col = j;
                    break OUT; // 跳出两个for循环
                }
            }
        }

    }

    private void initMenu() {
        JMenuBar menuBar = new JMenuBar(); // 创建一个菜单栏
        JMenu menu = new JMenu("系统");
        JMenuItem exitJi = new JMenuItem("退出");
        menu.add(exitJi); // 添加子菜单
        exitJi.addActionListener(e -> { // 添加点击事件
            // 退出游戏
            dispose();
        });
        JMenuItem restartJi = new JMenuItem("重启");
        menu.add(restartJi);
        restartJi.addActionListener(e -> { // 添加点击事件
            // 重启游戏
        });
        menuBar.add(menu); // 添加到菜单栏中
        this.setJMenuBar(menuBar);
    }

    private void initImage() {
        // 先清空窗口上的全部图层
        this.getContentPane().removeAll();

        // 1、展示一个行列矩阵的图片色块依次铺满窗口(4 * 4)
        for (int i = 0; i < imageData.length; i++) { // 遍历行
            for (int j = 0; j < imageData[i].length; j++) { // 遍历列
                // 拿到图片的名称
                String imageName = imageData[i][j] + ".png";
                // 2、创建一个JLabel对象,设置图片给他展示。
                JLabel label = new JLabel();
                // 3、设置图片到label对象中去。
                label.setIcon(new ImageIcon(imagePath + imageName));
                // 4、设置数字色块的位置
                label.setBounds(20 + j * 100, 60 + i * 100, 100, 100);
                // 5、把这个图片展示到窗口中
                this.add(label);
            }
        }

        // 设置窗口的背景图片
        JLabel background = new JLabel(new ImageIcon(imagePath + "background.png"));
        background.setBounds(0, 0, 450, 484);
        this.add(background);

        // 刷新新图层,重新绘制窗口
        this.repaint();
    }

    private void initFrame() {
        // 设置窗口标题
        this.setTitle("石子迷宫 V 1.0");
        // 设置窗口大小
        this.setSize(463, 543);
        // 设置窗口关闭方式
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // 设置窗口的居中显示
        this.setLocationRelativeTo(null);
        // 设置布局方式为绝对位置定位
        this.setLayout(null);
    }
}

4、判断是否通关

∙ \bullet 用户每操作一步,需要立即判断是否已经通关,如果通过,需要显示胜利的标记。
在这里插入图片描述

完成代码如下:

package com.itheima;

import javax.swing.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;

// 自定义窗口类,创建对象,展示一个主窗口。
public class MainFrame extends JFrame {
    // 设置图片位置
    private static final String imagePath = "stone-maze/src/image/";
    // 准备一个数组,用户存储数字色块的行列位置:4行4列
    private int[][] imageData = {
            {1, 2, 3, 4},
            {5, 6, 7, 8},
            {9, 10, 11, 12},
            {13, 14, 15, 0}
    };

    // 定义一个二维数组,用于存储最终游戏成功的数据顺序
    private int[][] winData = new int[][] {
            {1, 2, 3, 4},
            {5, 6, 7, 8},
            {9, 10, 11, 12},
            {13, 14, 15, 0}
    };

    // 定义两个整数变量记录当前空白色块的位置。
    private int row; // 当前空白色块的行位置
    private int col; // 当前空白色块的列位置

    public MainFrame() {
        // 1、调用一个初始化方法:初始化窗口大小等信息。
        initFrame();
        // 4、打乱数组色块的顺序,再提示图片
        initRandomArray();
        // 2、初始化界面:展示数字色块。
        initImage();
        // 3、初始化系统菜单:点击弹出菜单信息是系统退出,重启游戏。
        initMenu();
        // 5、给当前窗口绑定上下左右按键事件。
        initKeyPressEvent();
        // 设置窗口可见
        this.setVisible(true);
    }

    private void initKeyPressEvent() {
        // 给当前窗口绑定上下左右按键事件。
        this.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                // 判断当前按键的编号
                int keyCode = e.getKeyCode();
                // 判断按键编号,上下左右
                switch (keyCode) {
                    case KeyEvent.VK_UP:
                        switchAndMove(Direction.UP);
                        // 用户按下上键,把图片上移。
                        break;
                    case KeyEvent.VK_DOWN:
                        switchAndMove(Direction.DOWN);
                        // 用户按下下键,把图片下移。
                        break;
                    case KeyEvent.VK_LEFT:
                        switchAndMove(Direction.LEFT);
                        // 用户按下左键,把图片左移。
                        break;
                    case KeyEvent.VK_RIGHT:
                        switchAndMove(Direction.RIGHT);
                        // 用户按下右键,把图片右移。
                        break;
                }
            }
        });
    }

    // 控制数据交换和图片移动
    private void switchAndMove(Direction r) {
        // 判断图片的方向,再控制图片移动。
        switch (r) {
            case UP:
                // 上交换的条件是行必须 < 3,然后才开始交换。
                if (row < imageData.length - 1) {
                    // 当前空白色块位置:rol  col
                    // 需要被交换的位置:row + 1  col
                    int temp = imageData[row][col];
                    imageData[row][col] = imageData[row + 1][col];
                    imageData[row + 1][col] = temp;
                    // 更新当前空白色块的位置。
                    row++;
                }
                break;
            case DOWN:
                if (row > 0) {
                    // 当前空白色块位置:row  col
                    // 需要被交换的位置:row - 1  col
                    int temp = imageData[row][col];
                    imageData[row][col] = imageData[row - 1][col];
                    imageData[row - 1][col] = temp;
                    // 更新当前空白色块的位置。
                    row--;
                }
                break;
            case LEFT:
                // 左交换的条件是列必须 < 3,然后才开始交换。
                if (col < imageData.length - 1) {
                    // 当前空白色块位置:row  col
                    // 需要被交换的位置:row  col + 1
                    int temp = imageData[row][col];
                    imageData[row][col] = imageData[row][col + 1];
                    imageData[row][col + 1] = temp;
                    // 更新当前空白色块的位置。
                    col++;
                }
                break;
            case RIGHT:
                if (col > 0) {
                    int temp = imageData[row][col];
                    imageData[row][col] = imageData[row][col - 1];
                    imageData[row][col - 1] = temp;
                    col--;
                }
                break;
        }
        // 重新刷新界面!!!
        initImage();
    }

    private void initRandomArray() {
        // 1、打乱数组
        for (int i = 0; i < imageData.length; i++) { // 遍历行
            for (int j = 0; j < imageData[i].length; j++) { // 遍历列
                // 随机两个行列位置,让这两个位置交换。
                int i1 = (int)(Math.random() * imageData.length);
                int j1 = (int)(Math.random() * imageData.length);

                int i2 = (int)(Math.random() * imageData.length);
                int j2 = (int)(Math.random() * imageData.length);

                int temp = imageData[i1][j1];
                imageData[i1][j1] = imageData[i2][j2];
                imageData[i2][j2] = temp;
            }
        }

        // 定义空白色块的位置。
        // 去二维数组中遍历每个数据,只要发现这个数据等于0,这个位置就是当前空白色块的位置。
        OUT:
        for (int i = 0; i < imageData.length; i++) { // 遍历行
            for (int j = 0; j < imageData[i].length; j++) { // 遍历列
                if (imageData[i][j] == 0) {
                    // 定位空白色块的位置
                    row = i;
                    col = j;
                    break OUT; // 跳出两个for循环
                }
            }
        }

    }

    private void initMenu() {
        JMenuBar menuBar = new JMenuBar(); // 创建一个菜单栏
        JMenu menu = new JMenu("系统");
        JMenuItem exitJi = new JMenuItem("退出");
        menu.add(exitJi); // 添加子菜单
        exitJi.addActionListener(e -> { // 添加点击事件
            // 退出游戏
            dispose();
        });
        JMenuItem restartJi = new JMenuItem("重启");
        menu.add(restartJi);
        restartJi.addActionListener(e -> { // 添加点击事件
            // 重启游戏
        });
        menuBar.add(menu); // 添加到菜单栏中
        this.setJMenuBar(menuBar);
    }

    private void initImage() {
        // 先清空窗口上的全部图层
        this.getContentPane().removeAll();

        // 判断是否赢了
        if (isWin()) {
            // 展示胜利的图片
            JLabel label = new JLabel(new ImageIcon(imagePath + "win.png"));
            label.setBounds(124, 230, 266, 88);
            this.add(label);
        }

        // 1、展示一个行列矩阵的图片色块依次铺满窗口(4 * 4)
        for (int i = 0; i < imageData.length; i++) { // 遍历行
            for (int j = 0; j < imageData[i].length; j++) { // 遍历列
                // 拿到图片的名称
                String imageName = imageData[i][j] + ".png";
                // 2、创建一个JLabel对象,设置图片给他展示。
                JLabel label = new JLabel();
                // 3、设置图片到label对象中去。
                label.setIcon(new ImageIcon(imagePath + imageName));
                // 4、设置数字色块的位置
                label.setBounds(20 + j * 100, 60 + i * 100, 100, 100);
                // 5、把这个图片展示到窗口中
                this.add(label);
            }
        }

        // 设置窗口的背景图片
        JLabel background = new JLabel(new ImageIcon(imagePath + "background.png"));
        background.setBounds(0, 0, 450, 484);
        this.add(background);

        // 刷新新图层,重新绘制窗口
        this.repaint();
    }

    private boolean isWin() {
        // 判断是否赢了
        for (int i = 0; i < imageData.length; i++) { // 遍历行
            for (int j = 0; j < imageData[i].length; j++) { // 遍历列
                // 判断当前遍历到的数据是否和winData中的数据是否一致
                if (imageData[i][j] != winData[i][j]) {
                    // 如果不一致,则返回false
                    return false;
                }
            }
        }
        // 如果一致,则返回true,则显示胜利的图片
        return true;
    }

    private void initFrame() {
        // 设置窗口标题
        this.setTitle("石子迷宫 V 1.0");
        // 设置窗口大小
        this.setSize(463, 543);
        // 设置窗口关闭方式
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        // 设置窗口的居中显示
        this.setLocationRelativeTo(null);
        // 设置布局方式为绝对位置定位
        this.setLayout(null);
    }
}

5、统计移动步骤,重启游戏

∙ \bullet 每成功移动一步,都需要累加一次步数
∙ \bullet 定义一个变量用于累加步数,并实时展示到界面上

6、拓展问题

∙ \bullet 数字华容道的乱序操作,并不是可以随意打乱的,必须满足一定的算法去打乱顺序,这样才是有解的,才能让玩家恢复到有序。有没有简单的算法???在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2303757.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Liunx(CentOS-6-x86_64)系统安装MySql(5.6.50)

一&#xff1a;安装Liunx&#xff08;CentOS-6-x86_64&#xff09; 安装Liunx&#xff08;CentOS-6-x86_64&#xff09; 二&#xff1a;下载MySql&#xff08;5.6.50&#xff09; MySql下载官网 二&#xff1a;安装MySql 2.1 将mysql上传到Liunx 文件地址 /usr/local/ 2…

Java Web开发实战与项目——开发一个在线论坛系统

在线论坛系统是一个常见的Web应用&#xff0c;通常具有用户注册、帖子发布、评论互动、消息推送等基本功能。开发这样一个系统&#xff0c;既涉及到前后端的技术栈选择&#xff0c;也需要考虑性能、扩展性等实际问题。本文将从设计论坛模块、实现消息推送与实时更新功能、以及优…

ubuntu24.04无法安装向日葵,提示依赖libgconf-2-4怎么办?

在向日葵官方下载的deb包&#xff0c;目前是SunloginClient_15.2.0.63062_amd64.deb&#xff0c;执行安装代码&#xff0c;如下&#xff1a; sudo < /span > dpkg< /span > -i< /span > SunloginClient_15< /span >.2< /span >.0< /span >…

Kubernetes 使用 Kube-Prometheus 构建指标监控 +飞书告警

1 介绍 Prometheus Operator 为 Kubernetes 提供了对 Prometheus 机器相关监控组件的本地部署和管理方案&#xff0c;该项目的目的是为了简化和自动化基于 Prometheus 的监控栈配置&#xff0c;主要包括以下几个功能&#xff1a; Kubernetes 自定义资源&#xff1a;使用 Kube…

WPF的页面设计和实用功能实现

目录 一、TextBlock和TextBox 1. 在TextBlock中实时显示当前时间 二、ListView 1.ListView显示数据 三、ComboBox 1. ComboBox和CheckBox组合实现下拉框多选 四、Button 1. 设计Button按钮的边框为圆角&#xff0c;并对指针悬停时的颜色进行设置 一、TextBlock和TextBox…

window安装MySQL5.7

1、下载MySQL5.7.24 浏览器打开&#xff1a; https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-5.7.24-winx64.zip 2、解压缩 下载下来的是一个压缩包&#xff0c;解压到你想放到的目录下面&#xff0c;我放的是“C:\MySQL” 3、配置MySQL环境变量 计算机右键 - 属性 …

数据结构:哈希表(二)

目录 一、哈希表 1、概念 二、哈希冲突 1、概念 2、冲突避免 &#xff08;1&#xff09;哈希函数设计 &#xff08;2&#xff09;负载因子调节 3、冲突解决 &#xff08;1&#xff09;闭散列 1、线性探测 2、二次探测 &#xff08;2&#xff09;开散列 4、哈希桶实…

blender笔记2

一、物体贴地 物体->变换->对齐物体 ->对齐弹窗(对齐模式&#xff1a;反方&#xff0c;相对于&#xff1a;场景原点&#xff0c;对齐&#xff1a;z)。 之后可以设置原点->原点--3d游标 二、面上有阴影 在编辑模式下操作过后&#xff0c;物体面有阴影。 数据-&g…

1.21作业

1 unserialize3 当序列化字符串中属性个数大于实际属性个数时&#xff0c;不会执行反序列化 外部如果是unserialize&#xff08;&#xff09;会调用wakeup&#xff08;&#xff09;方法&#xff0c;输出“bad request”——构造url绕过wakeup 类型&#xff1a;public class&…

【Quest开发】全身跟踪(一)

软件&#xff1a;Unity 2022.3.51f1c1、vscode、Meta XR All in One SDK V72 硬件&#xff1a;Meta Quest3 最终效果&#xff1a;能像meta的操作室沉浸场景一样根据头盔移动来推断用户姿势&#xff0c;实现走路、蹲下、手势匹配等功能 需要借助UnityMovement这个包 GitHub …

最新版本Exoplayer扩展FFmpeg音频软解码保姆级教程

ExoPlayer 是一个开源的 Android 媒体播放库&#xff0c;由 Google 开发和维护&#xff0c;用于替代 Android 系统自带的 MediaPlayer。它提供了更强大的功能、更好的性能和更高的灵活性&#xff0c;适用于各种复杂的媒体播放场景。所以被广泛用于各种播放器场景。 最近项目中…

JS:页面事件

文章目录 一、页面加载事件二、页面滚动事件三、页面尺寸事件总结 一、页面加载事件 有时候我们会把script的内容放在body前&#xff0c;这时候代码的执行在元素的加载之前&#xff0c;会导致页面元素未加载而报错 解决办法是调用Window的load加载事件&#xff0c;将所有操作放…

vue,vue3 keepalive没有效果,无法缓存页面include无效,keep-alive

keepalive没有效果&#xff0c;无法缓存页面&#xff1f; 问题大概是组件的name值不对应&#xff0c;vue2修改组件文件的name值&#xff0c;vue3保持组件文件名称和路由页面配置的name一致就可以了&#xff0c;如果vue3不想保持一致&#xff0c;必须手动在文件后面添加export..…

DeepSeek智能测试知识库助手PRO版:多格式支持+性能优化

前言 测试工程师在管理测试资产时,需要面对多种文档格式、大量文件分类及知识库的构建任务。为了解决这些问题,我们升级了 DeepSeek智能测试知识库助手,不仅支持更多文档格式,还加入了 多线程并发处理 和 可扩展格式支持,大幅提升处理性能和灵活性。 主要功能亮点: 多格…

纯手工搭建整套CI/CD流水线指南

目录 一、前言 二、环境准备 1、服务器开荒&#xff08;192.168.1.200&#xff09; 2、离线资源清单&#xff08;提前用U盘拷好&#xff09; 三、硬核安装&#xff1a;比拧螺丝还细的步骤 Step1&#xff1a;搭建GitLab&#xff08;注意&#xff01;这是只内存饕餮&#xf…

智能硬件新时代,EasyRTC开启物联音视频新纪元

在万物互联的时代浪潮中&#xff0c;智能硬件正以前所未有的速度融入我们的生活&#xff0c;从智能家居的便捷控制&#xff0c;到智能穿戴设备的健康监测&#xff0c;再到工业物联网的高效管理&#xff0c;智能硬件的应用场景不断拓展。而在这个智能硬件蓬勃发展的背后&#xf…

Rust编程语言入门教程(八)所有权 Stack vs Heap

Rust 系列 &#x1f380;Rust编程语言入门教程&#xff08;一&#xff09;安装Rust&#x1f6aa; &#x1f380;Rust编程语言入门教程&#xff08;二&#xff09;hello_world&#x1f6aa; &#x1f380;Rust编程语言入门教程&#xff08;三&#xff09; Hello Cargo&#x1f…

交易所开发:数字市场的核心动力

数字资产交易所作为连接用户与市场的核心枢纽&#xff0c;已成为推动数字经济发展的关键引擎。其开发不仅需要技术创新&#xff0c;还需兼顾用户体验、合规安全与生态构建&#xff0c;以下是交易所开发的核心要素与实践路径分析&#xff1a; 一、交易所的核心定位与技术架构…

Jmeter进阶篇(34)如何解决jmeter.save.saveservice.timestamp_format=ms报错?

问题描述 今天使用Jmeter完成压测执行,然后使用命令将jtl文件转换成html报告时,遇到了报错! 大致就是说jmeter里定义了一个jmeter.save.saveservice.timestamp_format=ms的时间格式,但是jtl文件中的时间格式不是标准的这个ms格式,导致无法正常解析。对于这个问题,有如下…

Navicat17详细安装教程(附最新版本安装包和补丁)2025最详细图文教程安装手册

目录 前言&#xff1a;为什么选择Navicat 17&#xff1f; 一、下载Navicat17安装包 二、安装Navicat 1.运行安装程序 2.启动安装 3.同意“协议” 4.设置安装位置 5.创建桌面图标 6.开始安装 7.安装完成 三、安装补丁 1.解押补丁包 2.在解压后的补丁包目录下找到“w…