一、写在前面
- 这学期才学的java,课设不会写游戏,只好做一个桌面宠物了。JavaFX更是第一次用,边学边做的,所以有不足的地方还请提出,感激不尽。
- 环境:jdk1.8.0_241, eclipse。
- 用javafx是因为它gif的显示效果很好,不会像swing那样闪烁或循环过快,用来做桌面宠物正合适。
- 宠物用的是罗小黑和比丢(墙裂推荐,超好看的国漫),lxh和biu分别是它们对应的图片文件夹,。
- 有四个类,包括Main(设置窗体),EventListener(处理点击事件和加载图片),UI(设置系统托盘、弹出菜单、聊天气泡和实现自定义功能),Move(实现自动行走)。
- 源代码:https://gitee.com/june_day/javafx_desktop_pet
二、窗体设置(Main类)
(一)类成员
private static ImageView imageView;
EventListener listen;
VBox messageBox;//聊天气泡
int petID = 1;//宠物ID。罗小黑=0,比丢=1
double xOffset = 0;
double yOffset = 0;
(二)图片容器
Image image = new Image(this.getClass().getResourceAsStream("/biu/biu0.gif"));//biu即比丢
ImageView imageView = new ImageView(image);
//设置容器的位置
imageView.setX(0);
imageView.setY(0);
imageView.setLayoutX(0);
imageView.setLayoutY(50);
//设置图片显示的大小
imageView.setFitHeight(150);
imageView.setFitWidth(150);
imageView.setPreserveRatio(true); //保留 width:height的比例
特别说明:加载相对路径的图片要用class.getResourceAsStream,不然打包成jar包运行时会报错:找不到文件路径!路径中第一个“/”是必需的,它表示类的根目录,类文件夹在此项目中与lxh文件夹和biu文件夹在同一级。
(三)创建UI类
UI ui = new UI(imageView, petID, listen,primaryStage);
ui.addMessageBox("你好吖~");
(四)创建pane, scene, stage
AnchorPane pane = new AnchorPane(ui.getMessageBox(),ui.getImageView());
Scene scene = new Scene(pane,400,400);
scene.setFill(null);
primaryStage.setScene(scene);
//设置窗体的初始位置
primaryStage.setX(850);
primaryStage.setY(400);
primaryStage.setAlwaysOnTop(true);//窗口总显示在最前
//因为最后要播放告别动画,所以要延缓关闭
primaryStage.setOnCloseRequest( event ->{event.consume(); ui.end();});
primaryStage.show();
对primaryStage.setOnCloseRequest( event ->{event.consume(); ui.end();});
的说明:
- 点击任务栏的“关闭窗口”时,播放告别动画,同时使托盘的图标也关闭。
- event.consume()是必需的,这样才能真正阻止Window Close事件的默认处理。
- 如果不设置告别动画,可以仅仅使用System.exit(0);来直接退出,不需要event.consume();
(五)设置窗体背景透明
imageView.setStyle("-fx-background:transparent;");
pane.setStyle("-fx-background:transparent;");
primaryStage.initStyle(StageStyle.TRANSPARENT);
这样做之后标题栏、边框、右上角的关闭等按钮也都没有了。
(六)让宠物可拖动
//先获取按下鼠标时的坐标p1,再将窗体坐标设为p1加拖动的位移量
pane.setOnMousePressed(event -> {
xOffset = event.getSceneX();
yOffset = event.getSceneY();
});
pane.setOnMouseDragged(event -> {
primaryStage.setX(event.getScreenX() - xOffset);
primaryStage.setY(event.getScreenY() - yOffset);
});
三、Main类完整代码
public class Main extends Application {
private static ImageView imageView;
EventListener listen;
VBox messageBox;//聊天气泡
int petID = 1;//宠物ID。罗小黑=0,比丢=1
double xOffset = 0;
double yOffset = 0;
public void start(Stage primaryStage) {
try {
/*
* 创建初始的图
* 加载相对路径的图片要用class.getResource,不然运行jar包时会报错:找不到文件路径!
* 路径中第一个“/”是必需的,它表示类的根目录,类文件夹在此项目中与lxh和biu在同一级
*/
Image image = new Image(this.getClass().getResourceAsStream("/biu/biu0.gif"));
imageView = new ImageView(image);
imageView.setX(0);
imageView.setY(0);
imageView.setLayoutX(0);
imageView.setLayoutY(50);
//设置图片显示的大小
imageView.setFitHeight(150);
imageView.setFitWidth(150);
//添加图片的点击事件
listen = new EventListener(imageView , petID);
imageView.addEventHandler(MouseEvent.MOUSE_CLICKED, listen);
imageView.setPreserveRatio(true); //保留 width:height的比例
imageView.setStyle("-fx-background:transparent;");//容器背景设为透明
UI ui = new UI(imageView, petID, listen,primaryStage);
ui.addMessageBox("你好吖~");
AnchorPane pane = new AnchorPane(ui.getMessageBox(),ui.getImageView());
pane.setStyle("-fx-background:transparent;");
//使窗体能拖动。先获取按下鼠标时的坐标p1,再将窗体坐标设为p1加拖动的位移量
pane.setOnMousePressed(event -> {
xOffset = event.getSceneX();
yOffset = event.getSceneY();
});
pane.setOnMouseDragged(event -> {
primaryStage.setX(event.getScreenX() - xOffset);
primaryStage.setY(event.getScreenY() - yOffset);
});
Scene scene = new Scene(pane,400,400);
scene.setFill(null);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
primaryStage.setScene(scene);
//设置窗体的初始位置
primaryStage.setX(850);
primaryStage.setY(400);
primaryStage.setAlwaysOnTop(true);//窗口总显示在最前
//修改任务栏图标
primaryStage.getIcons().add(new Image(getClass().getResourceAsStream("icon.png")));
//下句隐藏任务栏图标,但javafx的stage.initStyle(Style)只能有一个起效,只好作罢
// stage.initStyle(StageStyle.UTILITY);
primaryStage.initStyle(StageStyle.TRANSPARENT);//背景透明
/*
* 点击任务栏的“关闭窗口”时,播放告别动画,同时使托盘的图标也关闭.
* event.consume()是必需的,这样才能真正阻止Window Close事件的默认处理。
* 如果仅仅使用System.exit(0);则不需要event.consume();
*/
primaryStage.setOnCloseRequest( event ->{event.consume(); ui.end();});
primaryStage.show();
ui.setTray(primaryStage);//添加系统托盘
Thread thread = new Thread(ui);
thread.start();
} catch(Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}