从点击到绚丽:手把手教你用JavaFX开发炫酷鼠标项目 - 附全套源码
目录
引言
在这篇博客中,我将分享如何使用JavaFX开发一个炫酷的鼠标特效应用,从项目架构到具体实现,完整记录开发过程中的经验和心得。
项目概述
Beautiful Mouse炫酷鼠标是一个基于JavaFX开发的桌面应用程序,通过全局鼠标事件监听,实现了多种炫酷的鼠标特效。用户可以根据个人喜好选择不同的特效样式,并且可以实时调整特效的参数,如颜色、大小、速度等。
项目地址
Gitee已开源:BeautifulMouse炫酷鼠标项目Giee开源地址
界面展示(精美UI布局)

技术栈
核心框架
- Java 17
- JavaFX:用于构建桌面GUI应用
- FXML:用于UI布局设计
- CSS:用于界面样式设计
项目管理
- Maven:项目构建和依赖管理
系统集成
- JNA:实现与操作系统的底层交互
- JNativeHook:实现全局鼠标事件监听
项目结构
beautifulMouse/
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/bm/beautifulmouse/
│ │ │ ├── MainApplication.java # 程序主入口
│ │ │ ├── controller/ # 控制器层
│ │ │ │ └── EffectController.java # 特效控制器
│ │ │ ├── effect/ # 特效实现层
│ │ │ │ ├── Effect.java # 特效接口
│ │ │ │ ├── StarEffect.java # 星星特效
│ │ │ │ ├── RainbowTrailEffect.java # 彩虹尾巴特效
│ │ │ │ ├── ParticleEffect.java # 粒子特效
│ │ │ │ ├── SnowEffect.java # 雪花特效
│ │ │ │ ├── HeartEffect.java # 心形特效
│ │ │ │ └── FirecrackerEffect.java # 爆竹特效
│ │ │ └── model/ # 模型层
│ │ │ └── EffectConfig.java # 特效配置类
│ │ └── resources/
│ │ ├── css/
│ │ │ ├── effect-style.css # 特效样式
│ │ │ └── style.css # 全局样式
│ │ ├── fxml/
│ │ │ ├── main.fxml # 主界面布局
│ │ │ └── effect-view.fxml # 特效控制面板布局
│ │ └── images/ # 图片资源
│ └── test/ # 测试目录
│ └── java/
│ └── com/bm/beautifulmouse/
│ └── effect/
│ └── EffectTest.java # 特效测试类
├── .gitignore # Git忽略文件
├── LICENSE # 开源协议
├── README.md # 项目说明
└── pom.xml # Maven配置文件
项目中所用到的设计模式
1.策略模式(Strategy Pattern)
1. 应用场景
在实现不同类型的鼠标特效时,每种特效都有其独特的生成算法和动画效果。
2. 具体实现
// 策略接口
public interface Effect {
void generate(Pane container, double x, double y, EffectConfig config);
}
// 具体策略实现
public class StarEffect implements Effect {
@Override
public void generate(Pane container, double x, double y, EffectConfig config) {
// 星星特效实现
}
}
public class RainbowTrailEffect implements Effect {
@Override
public void generate(Pane container, double x, double y, EffectConfig config) {
// 彩虹尾巴特效实现
}
}
3. 优势
-
特效算法可以独立变化
-
易于添加新的特效类型
-
避免使用复杂的条件语句
2.观察者模式(Observer Pattern)
1. 应用场景
用户通过控制面板调整特效参数时,需要实时更新特效显示。
2. 具体实现
public class EffectController {
private void setupListeners() {
// 观察参数变化
sizeSlider.valueProperty().addListener((obs, oldVal, newVal) -> {
config.setSize(newVal.doubleValue());
updateEffect();
});
colorPicker.valueProperty().addListener((obs, oldVal, newVal) -> {
config.setColor(newVal);
updateEffect();
});
}
}
3. 优势
-
实现了UI和特效逻辑的解耦
-
支持参数的实时预览
-
便于添加新的监听行为
整体代码
1.程序入口
package com.bm.beautifulmouse;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Screen;
import javafx.stage.Stage;
import org.jnativehook.GlobalScreen;
import org.jnativehook.NativeHookException;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* 应用程序主入口类
*/
public class MainApplication extends Application {
@Override
public void start(Stage stage) throws Exception {
// 初始化全局鼠标监听
initializeGlobalMouseHook();
// 加载主界面
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/fxml/main.fxml"));
Scene scene = new Scene(fxmlLoader.load());
// 添加样式表
scene.getStylesheets().addAll(
getClass().getResource("/css/style.css").toExternalForm(),
getClass().getResource("/css/effect-style.css").toExternalForm()
);
// 获取屏幕尺寸
Screen screen = Screen.getPrimary();
double screenWidth = screen.getBounds().getWidth();
double screenHeight = screen.getBounds().getHeight();
// 配置舞台
stage.setTitle("Beautiful Mouse");
stage.setScene(scene);
stage.setMaximized(true); // 设置最大化
stage.setMinWidth(800); // 设置最小宽度
stage.setMinHeight(600); // 设置最小高度
// 显示舞台
stage.show();
}
private void initializeGlobalMouseHook() {
Logger.getLogger(GlobalScreen.class.getPackage().getName()).setLevel(Level.OFF);
try {
GlobalScreen.registerNativeHook();
} catch (NativeHookException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
2.特效控制器
package com.bm.beautifulmouse.controller;
import com.bm.beautifulmouse.effect.Effect;
import com.bm.beautifulmouse.effect.ParticleEffect;
import com.bm.beautifulmouse.effect.SnowEffect;
import com.bm.beautifulmouse.effect.StarEffect;
import com.bm.beautifulmouse.effect.HeartEffect;
import com.bm.beautifulmouse.effect.PawEffect;
import com.bm.beautifulmouse.effect.FirecrackerEffect;
import com.bm.beautifulmouse.effect.RainbowTrailEffect;
import com.bm.beautifulmouse.model.EffectConfig;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
public class EffectController {
@FXML private Pane effectPane;
@FXML private ComboBox<String> effectTypeCombo;
@FXML private ColorPicker colorPicker;
@FXML private Slider sizeSlider;
@FXML private Slider speedSlider;
@FXML private Slider rangeSlider;
@FXML private Slider particleCountSlider;
// 标签引用
@FXML private Label sizeLabel;
@FXML private Label speedLabel;
@FXML private Label rangeLabel;
@FXML private Label particleCountLabel;
private Effect currentEffect;
private EffectConfig config;
@FXML
public void initialize() {
// 初始化配置
config = new EffectConfig();
currentEffect = new SnowEffect(); // 设置初始特效为雪花特效
// 设置下拉框样式和内容
effectTypeCombo.getItems().addAll("雪花特效", "粒子特效", "星星特效", "心形特效", "猫爪特效", "爆竹特效", "彩虹尾巴");
effectTypeCombo.setValue("雪花特效");
effectTypeCombo.getStyleClass().add("custom-combo-box");
// 设置滑块的默认值和样式
sizeSlider.setValue(1.0);
speedSlider.setValue(1.0);
rangeSlider.setValue(1.0);
particleCountSlider.setValue(5.0);
// 添加滑块样式类
sizeSlider.getStyleClass().add("custom-slider");
speedSlider.getStyleClass().add("custom-slider");
rangeSlider.getStyleClass().add("custom-slider");
particleCountSlider.getStyleClass().add("custom-slider");
// 设置颜色选择器默认颜色和样式
colorPicker.setValue(Color.WHITE);
colorPicker.getStyleClass().add("custom-color-picker");
// 添加事件监听器
setupEventListeners();
// 添加鼠标移动事件监听
effectPane.setOnMouseMoved(event -> {
if (currentEffect instanceof RainbowTrailEffect ||
currentEffect instanceof StarEffect) { // 添加StarEffect判断
// 更新配置
config.setColor(colorPicker.getValue());
config.setSize(sizeSlider.getValue());
config.setSpeed(speedSlider.getValue());
config.setRange(rangeSlider.getValue());
config.setParticleCount((int) particleCountSlider.getValue());
// 生成特效
currentEffect.generate(effectPane, event.getX(), event.getY(), config);
}
});
}
private void setupEventListeners() {
// 添加点击事件监听器
effectPane.setOnMouseClicked(event -> {
if (currentEffect != null) {
// 更新配置
config.setColor(colorPicker.getValue());
config.setSize(sizeSlider.getValue());
config.setSpeed(speedSlider.getValue());
config.setRange(rangeSlider.getValue());
config.setParticleCount((int) particleCountSlider.getValue());
// 生成特效
currentEffect.generate(effectPane, event.getX(), event.getY(), config);
}
});
// 特效类型切换监听
effectTypeCombo.setOnAction(e -> {
switch(effectTypeCombo.getValue()) {
case "雪花特效":
currentEffect = new SnowEffect();
break;
case "粒子特效":
currentEffect = new ParticleEffect();
break;
case "星星特效":
currentEffect = new StarEffect();
break;
case "心形特效":
currentEffect = new HeartEffect();
break;
case "猫爪特效":
currentEffect = new PawEffect();
break;
case "爆竹特效":
currentEffect = new FirecrackerEffect();
break;
case "彩虹尾巴":
currentEffect = new RainbowTrailEffect();
break;
}
});
// 其他控件值变化监听器
colorPicker.setOnAction(e -> config.setColor(colorPicker.getValue()));
sizeSlider.valueProperty().addListener((obs, old, newVal) ->
config.setSize(newVal.doubleValue()));
speedSlider.valueProperty().addListener((obs, old, newVal) ->
config.setSpeed(newVal.doubleValue()));
rangeSlider.valueProperty().addListener((obs, old, newVal) ->
config.setRange(newVal.doubleValue()));
particleCountSlider.valueProperty().addListener((obs, old, newVal) ->
config.setParticleCount(newVal.intValue()));
}
/**
* 重置为默认设置
*/
@FXML
private void resetToDefault() {
colorPicker.setValue(Color.WHITE);
sizeSlider.setValue(1.0);
speedSlider.setValue(1.0);
rangeSlider.setValue(50);
particleCountSlider.setValue(5);
}
/**
* 设置炫丽预设
*/
@FXML
private void setFancyPreset() {
colorPicker.setValue(Color.MAGENTA);
sizeSlider.setValue(1.5);
speedSlider.setValue(1.8);
rangeSlider.setValue(75);
particleCountSlider.setValue(30);
}
/**
* 设置柔和预设
*/
@FXML
private void setSoftPreset() {
colorPicker.setValue(Color.SKYBLUE);
sizeSlider.setValue(0.8);
speedSlider.setValue(0.6);
rangeSlider.setValue(40);
particleCountSlider.setValue(15);
}
/**
* 设置控件初始值
*/
private void setupControls() {
// 设置特效类型选项
effectTypeCombo.getItems().addAll(
"星星特效",
"彩虹尾巴",
"粒子特效",
"雪花特效",
"心形特效"
);
effectTypeCombo.setValue("星星特效");
// 设置默认值
resetToDefault();
}
/**
* 设置监听器
*/
private void setupListeners() {
// 大小滑块监听
sizeSlider.valueProperty().addListener((obs, oldVal, newVal) -> {
config.setSize(newVal.doubleValue());
sizeLabel.setText(String.format("%.1f", newVal.doubleValue()));
});
// 速度滑块监听
speedSlider.valueProperty().addListener((obs, oldVal, newVal) -> {
config.setSpeed(newVal.doubleValue());
speedLabel.setText(String.format("%.1f", newVal.doubleValue()));
});
// 范围滑块监听
rangeSlider.valueProperty().addListener((obs, oldVal, newVal) -> {
config.setRange(newVal.doubleValue());
rangeLabel.setText(String.format("%.0f", newVal.doubleValue()));
});
// 粒子数量滑块监听
particleCountSlider.valueProperty().addListener((obs, oldVal, newVal) -> {
config.setParticleCount(newVal.intValue());
particleCountLabel.setText(String.format("%d", newVal.intValue()));
});
// 颜色选择器监听
colorPicker.valueProperty().addListener((obs, oldVal, newVal) -> {
config.setColor(newVal);
});
}
}
3.特效接口
import com.bm.beautifulmouse.model.EffectConfig;
import javafx.scene.layout.Pane;
/**
* 特效接口
* 所有具体特效实现都需要实现此接口
*/
public interface Effect {
/**
* 生成特效的方法
* @param container 特效容器,用于放置特效元素
* @param x 鼠标X坐标
* @param y 鼠标Y坐标
* @param config 特效配置参数
*/
void generate(Pane container, double x, double y, EffectConfig config);
}
4.彩虹尾巴特效
package com.bm.beautifulmouse.effect;
import com.bm.beautifulmouse.model.EffectConfig;
import javafx.animation.*;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.*;
import javafx.util.Duration;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
public class RainbowTrailEffect implements Effect {
private final Random random = new Random();
private final Queue<Circle> particles = new LinkedList<>();
private final Queue<Timeline> animations = new LinkedList<>();
private Circle lastParticle = null;
private static final int MAX_PARTICLES = 50;
private static final Color[] RAINBOW_COLORS = {
Color.RED, Color.ORANGE, Color.YELLOW,
Color.GREEN, Color.BLUE, Color.PURPLE
};
@Override
public void generate(Pane container, double x, double y, EffectConfig config) {
// 创建新的粒子
Circle particle = createParticle(x, y, config);
container.getChildren().add(particle);
// 如果存在上一个粒子,创建连线
if (lastParticle != null) {
Line connection = createConnection(lastParticle, particle);
container.getChildren().add(connection);
// 为连线创建消失动画
Timeline connectionTimeline = new Timeline(
new KeyFrame(Duration.ZERO, new KeyValue(connection.opacityProperty(), 0.5)),
new KeyFrame(Duration.seconds(0.5), new KeyValue(connection.opacityProperty(), 0))
);
connectionTimeline.setOnFinished(e -> container.getChildren().remove(connection));
connectionTimeline.play();
}
// 更新最后一个粒子
lastParticle = particle;
particles.offer(particle);
// 创建粒子动画
Timeline timeline = createAnimation(container, particle, config);
animations.offer(timeline);
timeline.play();
// 添加旋转效果
addRotation(particle);
// 清理旧的粒子
cleanOldParticles(container);
}
private Circle createParticle(double x, double y, EffectConfig config) {
Circle particle = new Circle(3 * config.getSize());
particle.setCenterX(x);
particle.setCenterY(y);
Color baseColor = RAINBOW_COLORS[random.nextInt(RAINBOW_COLORS.length)];
Color randomColor = baseColor.deriveColor(
random.nextDouble() * 360,
0.8 + random.nextDouble() * 0.2,
0.8 + random.nextDouble() * 0.2,
1.0
);
particle.setFill(randomColor);
particle.setEffect(new javafx.scene.effect.Glow(0.8));
return particle;
}
private Line createConnection(Circle start, Circle end) {
Line connection = new Line();
connection.startXProperty().bind(start.centerXProperty());
connection.startYProperty().bind(start.centerYProperty());
connection.endXProperty().bind(end.centerXProperty());
connection.endYProperty().bind(end.centerYProperty());
connection.setStroke(start.getFill());
connection.setStrokeWidth(2);
connection.setOpacity(0.5);
return connection;
}
private void addRotation(Circle particle) {
RotateTransition rotate = new RotateTransition(Duration.seconds(1), particle);
rotate.setByAngle(360);
rotate.setCycleCount(Timeline.INDEFINITE);
rotate.play();
}
private Timeline createAnimation(Pane container, Circle particle, EffectConfig config) {
Timeline timeline = new Timeline(
new KeyFrame(Duration.ZERO,
new KeyValue(particle.opacityProperty(), 1.0),
new KeyValue(particle.radiusProperty(), 3 * config.getSize())
),
new KeyFrame(Duration.seconds(0.5 / config.getSpeed()),
new KeyValue(particle.opacityProperty(), 0.0),
new KeyValue(particle.radiusProperty(), config.getSize())
)
);
timeline.setOnFinished(e -> {
container.getChildren().remove(particle);
particles.remove(particle);
animations.remove(timeline);
if (lastParticle == particle) {
lastParticle = null;
}
});
return timeline;
}
private void cleanOldParticles(Pane container) {
while (particles.size() >= MAX_PARTICLES) {
Circle oldParticle = particles.poll();
Timeline oldAnimation = animations.poll();
if (oldAnimation != null) {
oldAnimation.stop();
}
container.getChildren().remove(oldParticle);
if (lastParticle == oldParticle) {
lastParticle = null;
}
}
}
}
5.特效配置类
import javafx.scene.paint.Color;
public class EffectConfig {
private Color color = Color.WHITE;
private double size = 1.0;
private int particleCount = 52;
private double speed = 1.55;
private double range = 1.55;
public Color getColor() { return color; }
public void setColor(Color color) { this.color = color; }
public double getSize() { return size; }
public void setSize(double size) { this.size = size; }
public int getParticleCount() { return particleCount; }
public void setParticleCount(int count) { this.particleCount = count; }
public double getSpeed() { return speed; }
public void setSpeed(double speed) { this.speed = speed; }
public double getRange() { return range; }
public void setRange(double range) { this.range = range; }
}
6.特效样式
/* 全局样式 */
.root {
-fx-background-color: #2b2b2b;
-fx-font-family: "Microsoft YaHei", "Segoe UI", Arial, sans-serif;
}
/* 标签样式 */
.label {
-fx-text-fill: #e8e8e8;
-fx-font-size: 14px;
-fx-padding: 5 0 5 0;
}
/* 下拉框样式 */
.custom-combo-box {
-fx-background-color: #3c3f41;
-fx-text-fill: white;
-fx-mark-color: #f0f0f0;
-fx-background-radius: 5;
}
.custom-combo-box .list-cell {
-fx-background-color: transparent;
-fx-text-fill: white;
-fx-padding: 5 10 5 10;
}
.custom-combo-box .list-view {
-fx-background-color: #3c3f41;
-fx-background-radius: 5;
}
.custom-combo-box .list-cell:hover {
-fx-background-color: #4c5052;
}
/* 滑块样式 */
.custom-slider {
-fx-padding: 10 0 10 0;
}
.custom-slider .track {
-fx-background-color: #515151;
-fx-background-radius: 2;
}
.custom-slider .thumb {
-fx-background-color: #6a9ec5;
-fx-background-radius: 50;
-fx-padding: 7;
}
.custom-slider:hover .thumb {
-fx-background-color: #7bafd4;
}
/* 颜色选择器样式 */
.custom-color-picker {
-fx-background-color: #3c3f41;
-fx-background-radius: 5;
}
.custom-color-picker .color-picker-label {
-fx-text-fill: white;
}
/* 控制面板容器样式 */
.control-panel {
-fx-background-color: rgba(43, 43, 43, 0.85);
-fx-background-radius: 10;
-fx-padding: 15;
-fx-spacing: 10;
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.4), 10, 0, 0, 0);
}
/* 分隔线样式 */
.separator {
-fx-background-color: #515151;
}
/* 动画效果 */
.custom-combo-box,
.custom-slider .thumb,
.custom-color-picker {
-fx-transition: -fx-background-color 0.2s ease-in-out;
}
/* 鼠标悬停效果 */
.custom-combo-box:hover,
.custom-color-picker:hover {
-fx-background-color: #4c5052;
}
7.全局样式
/* 全局样式定义 */
/* 根节点样式 */
.root {
/* 主题颜色 */
-fx-primary: #2196F3;
-fx-primary-light: #64B5F6;
-fx-primary-dark: #1976D2;
-fx-accent: #FF4081;
/* 背景颜色 */
-fx-background: #121212;
-fx-background-light: #1E1E1E;
/* 文本颜色 */
-fx-text-primary: #FFFFFF;
-fx-text-secondary: rgba(255, 255, 255, 0.7);
/* 默认字体 */
-fx-font-family: "Microsoft YaHei";
-fx-font-size: 14px;
}
/* 按钮样式 */
.button {
-fx-background-color: -fx-primary;
-fx-text-fill: white;
-fx-background-radius: 4px;
-fx-padding: 8px 16px;
-fx-cursor: hand;
}
.button:hover {
-fx-background-color: -fx-primary-light;
}
.button:pressed {
-fx-background-color: -fx-primary-dark;
}
/* 滑块样式 */
.slider {
-fx-show-tick-labels: true;
-fx-show-tick-marks: true;
-fx-major-tick-unit: 0.5;
-fx-block-increment: 0.1;
}
.slider .track {
-fx-background-color: -fx-background-light;
}
.slider .thumb {
-fx-background-color: -fx-primary;
}
/* 下拉框样式 */
.combo-box {
-fx-background-color: -fx-background-light;
-fx-text-fill: -fx-text-primary;
-fx-background-radius: 4px;
-fx-padding: 5px;
}
.combo-box .list-cell {
-fx-background-color: -fx-background-light;
-fx-text-fill: -fx-text-primary;
}
.combo-box .list-view {
-fx-background-color: -fx-background-light;
}
/* 颜色选择器样式 */
.color-picker {
-fx-background-color: -fx-background-light;
-fx-background-radius: 4px;
-fx-min-height: 35px;
}
/* 标签样式 */
.label {
-fx-text-fill: -fx-text-primary;
}
/* 面板样式 */
.pane {
-fx-background-color: -fx-background;
}
/* 工具提示样式 */
.tooltip {
-fx-background-color: -fx-background-light;
-fx-text-fill: -fx-text-primary;
-fx-font-size: 12px;
}
/* 滚动条样式 */
.scroll-bar {
-fx-background-color: transparent;
}
.scroll-bar .thumb {
-fx-background-color: -fx-primary;
-fx-background-radius: 4px;
}
/* 分隔线样式 */
.separator {
-fx-background-color: rgba(255, 255, 255, 0.1);
}
/* 状态栏样式 */
.status-bar {
-fx-background-color: -fx-background-light;
-fx-padding: 4px;
}
/* 控制面板样式 */
.control-panel {
-fx-background-color: rgba(43, 43, 43, 0.85);
-fx-background-radius: 10 0 0 10;
-fx-effect: dropshadow(gaussian, rgba(0, 0, 0, 0.4), 10, 0, -5, 0);
}
/* 面板标题 */
.panel-title {
-fx-font-size: 18px;
-fx-font-weight: bold;
-fx-text-fill: -fx-text-primary;
}
/* 控制标签 */
.control-label {
-fx-font-size: 14px;
-fx-text-fill: -fx-text-secondary;
}
/* 值标签 */
.value-label {
-fx-font-size: 12px;
-fx-text-fill: -fx-text-secondary;
-fx-alignment: CENTER-RIGHT;
}
/* 预设按钮 */
.preset-button {
-fx-min-width: 80px;
-fx-background-color: -fx-primary;
-fx-text-fill: white;
}
.preset-button:hover {
-fx-background-color: -fx-primary-light;
}
/* 版本标签 */
.version-label {
-fx-font-size: 12px;
-fx-text-fill: -fx-text-secondary;
-fx-alignment: CENTER;
}
/* 滚动面板样式 */
.control-panel-scroll {
-fx-background: transparent;
-fx-background-color: transparent;
-fx-border-width: 0;
}
.control-panel-scroll > .viewport {
-fx-background-color: transparent;
}
.control-panel-scroll > .scroll-bar:vertical {
-fx-background-color: transparent;
-fx-padding: 0 0 0 1;
}
.control-panel-scroll > .scroll-bar:vertical > .thumb {
-fx-background-color: rgba(255, 255, 255, 0.3);
-fx-background-radius: 2;
}
.control-panel-scroll > .scroll-bar:vertical > .track {
-fx-background-color: transparent;
}
/* 确保控制面板始终显示在右侧 */
.control-panel-scroll {
-fx-fit-to-height: true;
-fx-hbar-policy: never;
-fx-background-insets: 0;
-fx-padding: 0;
}
/* 主容器样式 */
.main-container {
-fx-background-color: -fx-background;
}
/* 特效面板样式 */
.effect-pane {
-fx-background-color: transparent;
}
8.主界面布局
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.*?>
<?import javafx.geometry.Insets?>
<BorderPane xmlns:fx="http://javafx.com/fxml/1"
xmlns="http://javafx.com/javafx/17"
fx:controller="com.bm.beautifulmouse.controller.EffectController">
<center>
<!-- 这是特效显示的主面板 -->
<Pane fx:id="effectPane" styleClass="effect-pane"/>
</center>
<right>
<VBox spacing="10" styleClass="config-panel">
<Label text="特效类型"/>
<ComboBox fx:id="effectTypeCombo" maxWidth="Infinity"/>
<Label text="特效颜色"/>
<ColorPicker fx:id="colorPicker"/>
<Label text="特效大小"/>
<Slider fx:id="sizeSlider" min="0.1" max="2.0" value="1.0"/>
<Label text="特效速度"/>
<Slider fx:id="speedSlider" min="0.1" max="3.0" value="1.55"/>
<Label text="特效范围"/>
<Slider fx:id="rangeSlider" min="0.1" max="3.0" value="1.55"/>
<Label text="粒子数量"/>
<Slider fx:id="particleCountSlider" min="5" max="100" value="52.5"/>
</VBox>
</right>
</BorderPane>
8.特效控制面板布局
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<AnchorPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="com.bm.beautifulmouse.controller.EffectController"
stylesheets="@../css/effect-style.css">
<VBox styleClass="control-panel" spacing="10" maxWidth="300">
<padding>
<Insets top="15" right="15" bottom="15" left="15"/>
</padding>
<Label text="特效类型"/>
<ComboBox fx:id="effectTypeCombo" maxWidth="Infinity"/>
<Separator/>
<Label text="颜色"/>
<ColorPicker fx:id="colorPicker" maxWidth="Infinity"/>
<Label text="大小"/>
<Slider fx:id="sizeSlider" min="0.1" max="2.0" value="1.0"/>
<Label text="速度"/>
<Slider fx:id="speedSlider" min="0.1" max="2.0" value="1.0"/>
<Label text="范围"/>
<Slider fx:id="rangeSlider" min="0.1" max="2.0" value="1.0"/>
<Label text="粒子数量"/>
<Slider fx:id="particleCountSlider" min="1" max="20" value="5"/>
</VBox>
</AnchorPane>
9.POM文件依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.bm</groupId>
<artifactId>beautiful-mouse</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<javafx.version>17.0.2</javafx.version>
<main.class>com.bm.beautifulmouse.MainApplication</main.class>
</properties>
<dependencies>
<!-- 本地 JavaFX 依赖 -->
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>${javafx.version}</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/lib/javafx.controls.jar</systemPath>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>${javafx.version}</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/lib/javafx.fxml.jar</systemPath>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-base</artifactId>
<version>${javafx.version}</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/lib/javafx.base.jar</systemPath>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-graphics</artifactId>
<version>${javafx.version}</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/lib/javafx.graphics.jar</systemPath>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-media</artifactId>
<version>${javafx.version}</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/lib/javafx.media.jar</systemPath>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-swing</artifactId>
<version>${javafx.version}</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/lib/javafx.swing.jar</systemPath>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-web</artifactId>
<version>${javafx.version}</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/lib/javafx.web.jar</systemPath>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-swt</artifactId>
<version>${javafx.version}</version>
<scope>system</scope>
<systemPath>${project.basedir}/src/main/resources/lib/javafx-swt.jar</systemPath>
</dependency>
<!-- 其他依赖 -->
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna</artifactId>
<version>5.13.0</version>
</dependency>
<dependency>
<groupId>net.java.dev.jna</groupId>
<artifactId>jna-platform</artifactId>
<version>5.13.0</version>
</dependency>
<dependency>
<groupId>com.1stleg</groupId>
<artifactId>jnativehook</artifactId>
<version>2.1.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version>
<configuration>
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
</configuration>
</plugin>
<plugin>
<groupId>io.github.fvarrui</groupId>
<artifactId>javapackager</artifactId>
<version>1.6.6</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>package</goal>
</goals>
<configuration>
<mainClass>com.bm.beautifulmouse.MainApplication</mainClass>
<!-- 只保留最基本的模块 -->
<modules>
<module>java.base</module>
<module>java.desktop</module>
<module>jdk.unsupported</module>
</modules>
<!-- 基本配置 -->
<bundleJre>true</bundleJre>
<generateInstaller>false</generateInstaller>
<administratorRequired>false</administratorRequired>
<!-- 启动参数 -->
<vmArgs>
<vmArg>--module-path "lib"</vmArg>
<vmArg>--add-modules javafx.controls,javafx.fxml</vmArg>
<vmArg>--add-opens java.base/java.lang=ALL-UNNAMED</vmArg>
</vmArgs>
<!-- 平台配置 -->
<platform>windows</platform>
<name>BeautifulMouse</name>
<displayName>${name}</displayName>
<!-- Windows 配置 -->
<winConfig>
<headerType>gui</headerType>
<icoFile>${project.basedir}/src/main/resources/icon.ico</icoFile>
</winConfig>
<!-- 资源配置 -->
<additionalResources>
<additionalResource>${project.basedir}/src/main/resources/lib</additionalResource>
</additionalResources>
<!-- JRE 配置 -->
<customizedJre>true</customizedJre>
<jrePath>F:\jdk-17.0.0.1</jrePath>
<!-- jlink 参数配置 -->
<jreMinVersion>17</jreMinVersion>
<linuxConfig>
<wrapJar>false</wrapJar>
</linuxConfig>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
部署说明
1. 源码获取
# 克隆项目
git clone https://gitee.com/xiongwenhao6/beautiful-mouse.git
2. IDE运行配置(IntelliJ IDEA)
配置VM选项
--module-path "项目路径/src/main/resources/lib" --add-modules javafx.controls,javafx.fxml
具体步骤:
-
打开 "Run/Debug Configurations"
-
选择 "Application" 配置
-
在 "VM options" 中添加以上参数
-
确保 "Main class" 设置为 com.bm.beautifulmouse.MainApplication
-
启动项目(保证依赖全部导入)
3. 生成exe可执行文件
# 1. 清理项目
mvn clean
# 2. 打包项目
mvn package
项目打包后会在targer目录下会生成BeautifulMouse目录,然后切换到这个目录执行BeautifulMouse.exe文件
直接运行
-
双击 BeautifulMouse.exe 文件
命令行运行
#切换到指定目录下
cd target/BeautifulMouse
#执行命令运行程序
.\BeautifulMouse.exe

常见问题解决
找不到JavaFX模块
Error: JavaFX runtime components are missing, and are required to run this application
解决方案:
-
确保JavaFX库文件存在于lib目录下
-
检查模块路径是否正确
-
验证JavaFX版本与JDK版本匹配
模块路径错误
Error: Module javafx.controls not found
解决方案:
-
检查lib目录是否包含所有必需的JavaFX jar文件
-
确保路径中不包含特殊字符
-
使用绝对路径替代相对路径
权限问题:
AccessDeniedException: Access is denied
解决方案:
-
以管理员身份运行
-
检查文件和目录权限
-
确保当前用户有足够的访问权限
结语
感谢您阅读本博客!希望本文对您理解和使用Beautiful Mouse炫酷鼠标项目有所帮助。如果您在使用过程中遇到任何问题或有任何建议,请随时在评论区留言,我们会尽快回复您。您的反馈对我们非常重要,能够帮助我们不断改进和优化项目。
如果您喜欢这个项目,请考虑分享给您的朋友或同事,让更多的人体验到这个炫酷的鼠标特效应用!再次感谢您的支持,祝您编程愉快!😊

浙公网安备 33010602011771号