preface
I am a person who likes to open the setting interface and try functions one by one. In the past, I tried to set the message notification of CSDN once a day. I thought, "no one pays attention anyway. It's the same as not setting it."
On the 23rd, I received an email saying that someone left me a message. I was very happy. I clicked on the page and suddenly found that I had 600 + unread messages, which frightened me
Thank you very much for your attention (* ^ ▽ ^ *)! This also gives me great motivation, so I haven't been idle in recent days. I work overtime to improve the functions a little bit.
Many people want to try something new with demo. I'm afraid everyone will be disappointed, so I didn't package it into an installation package, because the first version is really very simple
If I really want to run, I'm not stingy, but I don't package it into an executable file. I also need to configure the Java environment to run. Oh, I put all the files of the project on GitHub. Click visit = > Jiang-TaiBai/IXiaoHei <=
Overview of the second edition
Generally, the following contents are made:
- Right click menu
- Set status class (mood, physical strength, cleanliness, etc.)
- Appliance warehouse
- Use effect of appliances
- Optimized the previous code and set more singleton modes (I don't know if it's good or not, it's easy to cause memory leakage, but the code is better to write, ha ha)
The following figure is a preview. I don't know why the card is playing on CSDN. Unfortunately, the video can't be plugged in (you have to upload the video link to other platforms)
The interface design is not good enough. I want to improve the functions in the early stage, and then settle down to sort out the interface (it's really difficult to find materials!!!)
Realization of right-click menu function
The basic idea is that when you right-click the ImageView Node where Luo Xiaohei is located, open an FXML rendering interface, and the FXML interface has to use a layer of nominalstage (no well named, literal translation is the nominal stage), so as to avoid adding a small logo on the taskbar.
The FXML sets many buttons, and each button corresponds to the method of the Controller.
Wake up right-click menu class
package org.taibai.hellohei.menu; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.event.EventType; import javafx.fxml.FXMLLoader; import javafx.geometry.Rectangle2D; import javafx.scene.Node; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.input.KeyEvent; import javafx.scene.paint.Color; import javafx.stage.Popup; import javafx.stage.Screen; import javafx.stage.Stage; import javafx.stage.StageStyle; import org.taibai.hellohei.controller.ContextMenuController; import java.io.IOException; /** * <p>Creation Time: 2021-09-25 04:36:35</p> * <p>Description: Click the right-click menu triggered by the ontology</p> * * @author Taibai */ public class ContextMenu { private static ContextMenu contextMenu; private ContextMenu() { } public static ContextMenu getInstance() { if (contextMenu == null) contextMenu = new ContextMenu(); return contextMenu; } public void show(Node node, double screenX, double screenY) { // ======Set the nominal stage to avoid generating a small window in the taskbar====== final Stage nominalStage = new Stage(); nominalStage.initStyle(StageStyle.UTILITY); nominalStage.setOpacity(0); final Stage stage = new Stage(); stage.initOwner(nominalStage); stage.initStyle(StageStyle.TRANSPARENT); // Sets the window to be transparent and borderless stage.setAlwaysOnTop(true); // The settings window is always displayed at the top // ======Set the position where the menu appears. By default, it appears at the lower right of the cursor, but there are two cases that exceed the edge of the screen====== Rectangle2D screenRectangle = Screen.getPrimary().getBounds(); double screenWidth = screenRectangle.getWidth(); double screenHeight = screenRectangle.getHeight(); double stageWidth = 138.0; double stageHeight = 280.0; // If the pop-up right-click menu exceeds the lower edge of the screen, it will expand upward if(screenY + stageHeight > screenHeight) screenY -= stageHeight; // If the pop-up right-click menu exceeds the right edge of the screen, it will expand to the left if(screenX + stageWidth > screenWidth) screenX -= stageWidth; stage.setX(screenX); stage.setY(screenY); // ======Get fxml file====== FXMLLoader loader = new FXMLLoader(getClass().getResource("/org/taibai/hellohei/fxml/ContextMenu.fxml")); Parent root = null; try { root = loader.load(); } catch (IOException e) { e.printStackTrace(); } // ======Get controller instance====== ContextMenuController controller = loader.getController(); //Gets the instance object of the Controller controller.Init(stage, screenX, screenY); // ======Load the scene in the stage and set the css style for the scene====== Scene scene = new Scene(root); scene.setFill(Color.TRANSPARENT); stage.setScene(scene); scene.getStylesheets().addAll(this.getClass().getResource("/org/taibai/hellohei/fxml/ContextMenu.css").toExternalForm()); // ======Set the hidden stage when the focus is lost====== stage.focusedProperty().addListener((observable, oldValue, newValue) -> { if (!stage.isFocused()) { stage.close(); } }); // ======Display menu====== nominalStage.show(); stage.show(); } }
Location of the right-click menu
This class is called [wake up right-click menu class], which is triggered by a click event. Therefore, you can obtain the coordinates of the cursor on the screen when the mouse clicks, so just set the coordinates in the upper left corner of the right-click menu.
Of course, just as we right-click on the desktop, if the right-click menu exceeds the screen, it should appear on the other side. Here, the following code is used to solve this requirement:
// ======Set the position where the menu appears. By default, it appears at the lower right of the cursor, but there are two cases that exceed the edge of the screen====== Rectangle2D screenRectangle = Screen.getPrimary().getBounds(); double screenWidth = screenRectangle.getWidth(); double screenHeight = screenRectangle.getHeight(); double stageWidth = 138.0; double stageHeight = 280.0; // If the pop-up right-click menu exceeds the lower edge of the screen, it will expand upward if(screenY + stageHeight > screenHeight) screenY -= stageHeight; // If the pop-up right-click menu exceeds the right edge of the screen, it will expand to the left if(screenX + stageWidth > screenWidth) screenX -= stageWidth; stage.setX(screenX); stage.setY(screenY);
Close the right-click menu
This function can sacrifice more than a dozen of my hair
I searched the whole network for countless solutions, and finally solved them. The content of JavaFx on the Internet is not as much as Spring. It can be said that it is difficult to do anything~
The solution is to listen to the focused attribute of the stage. Once found! stage.isFocused(), close the stage
stage.focusedProperty().addListener((observable, oldValue, newValue) -> { if (!stage.isFocused()) { stage.close(); } });
Right click menu controller class
The right-click menu controller class mainly controls the trigger event of the button. At present, it only realizes feeding and bathing. The reason why I didn't see a doctor is because I didn't find the material
It's all right. The template is set up. The medical module can be completed soon after the materials are available. Other modules still need time to precipitate.
package org.taibai.hellohei.controller; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.geometry.Rectangle2D; import javafx.scene.Node; import javafx.scene.Parent; import javafx.scene.Scene; import javafx.scene.paint.Color; import javafx.stage.Screen; import javafx.stage.Stage; import javafx.stage.StageStyle; import java.io.IOException; /** * <p>Creation Time: 2021-09-25 10:38:00</p> * <p>Description: Controller in right-click menu</p> * * @author Taibai */ public class ContextMenuController { /** * The first level menu should be hidden after clicking the button */ private Stage preStage; /** * The X coordinate in the upper left corner of the open menu is used to open the secondary menu */ private double screenX; /** * The Y coordinate in the upper left corner of the open menu is used to open the secondary menu */ private double screenY; public void Init(Stage stage, double screenX, double screenY) { this.preStage = stage; this.screenX = screenX; this.screenY = screenY; } @FXML public void eat() { preStage.close(); showItemsWindow(ItemsWindowController.FoodTitle); } @FXML public void bath() { preStage.close(); showItemsWindow(ItemsWindowController.BathTitle); } private void showItemsWindow(String title) { // ======Set the nominal stage to avoid generating a small window in the taskbar====== final Stage nominalStage = new Stage(); nominalStage.initStyle(StageStyle.UTILITY); nominalStage.setOpacity(0); final Stage stage = new Stage(); stage.initOwner(nominalStage); stage.initStyle(StageStyle.TRANSPARENT); // Sets the window to be transparent and borderless stage.setAlwaysOnTop(true); // The settings window is always displayed at the top // ======Set the position where the menu appears. By default, it appears at the lower right of the cursor, but there are two cases that exceed the edge of the screen====== stage.setX(screenX); stage.setY(screenY); // ======Get fxml file====== FXMLLoader loader = new FXMLLoader(getClass().getResource("/org/taibai/hellohei/fxml/ItemsWindow.fxml")); Parent root = null; try { root = loader.load(); } catch (IOException e) { e.printStackTrace(); } // ======Get controller instance====== ItemsWindowController controller = loader.getController(); //Gets the instance object of the Controller controller.Init(title, stage); // ======Load the scene in the stage and set the css style for the scene====== Scene scene = new Scene(root); scene.setFill(Color.TRANSPARENT); stage.setScene(scene); scene.getStylesheets().addAll(this.getClass().getResource("/org/taibai/hellohei/fxml/ItemsWindow.css").toExternalForm()); // ======Set the hidden stage when the focus is lost====== stage.focusedProperty().addListener((observable, oldValue, newValue) -> { if (!stage.isFocused()) { stage.close(); } }); // ======Display menu====== nominalStage.show(); stage.show(); } }
This is almost as like as two peas. The function is to create a menu to close.
This class leads to the ItemsWindowController class, which is the controller that controls the menu
Item display menu bar
UI design of item display menu bar
Users need to select items to trigger the use of items (milk and eggs appear in the first episode ~ they can be replaced with corresponding animation when they have time to deduct them in the future). Then the content in it must not be dead, so I designed such a page (after several experiments T_T, I still don't know how to add a scroll bar to vbox):
From top to bottom:
- The outermost anchor pane is the panel of the entire item list
- The words in the Label only need to be replaced and can be reused as a list of food, bath products and drugs
- Pane... Now think about it. It seems that there is no need to set it. Wait for the third version of optimization
- ScrollPane is a scrolling pane so that if there are many items in it, you can scroll through them
- VBox is especially convenient for placing items. You don't have to calculate xy coordinates yourself
Item display menu bar controller class
At present, is too laggy to open the two level menu in the second edition. Generally, the analysis should be caused by frequent deletion of nodes. I hope that the third version can cache the node organization structure.
package org.taibai.hellohei.controller; import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.Label; import javafx.scene.control.ScrollPane; import javafx.scene.layout.*; import javafx.stage.Stage; import org.taibai.hellohei.items.bath.BathItem; import org.taibai.hellohei.items.food.FoodItem; import org.taibai.hellohei.items.ItemWarehouse; import org.taibai.hellohei.state.TotalState; import java.util.Map; /** * <p>Creation Time: 2021-09-27 00:32:16</p> * <p>Description: Goods list controller. This window is used to display the list of food, bath supplies and workers</p> * * @author Taibai */ public class ItemsWindowController { public static final String FoodTitle = "Food warehouse"; public static final String BathTitle = "Bath warehouse"; public static final String DrugTitle = "Drug warehouse"; @FXML public Label title; private String titleText; @FXML public Pane itemPane; @FXML public ScrollPane scrollPane; @FXML public VBox vbox; private Stage stage; public void Init(String title, Stage stage) { this.stage = stage; this.titleText = title; this.title.setText(title); vbox.setAlignment(Pos.TOP_CENTER); vbox.setSpacing(10); // Disable left and right rollers scrollPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); loadItems(); } private void loadItems() { switch (titleText) { case FoodTitle: loadFoodItems(); break; case BathTitle: loadBathItems(); break; case DrugTitle: loadDrugItems(); } } private void loadFoodItems() { Map<String, FoodItem> foodItemList = ItemWarehouse.getInstance().getFoodItemMap(); for (Map.Entry<String, FoodItem> entry : foodItemList.entrySet()) { if (entry.getValue().getItemNum() != 0) { vbox.getChildren().add(entry.getValue().toItemAnchorPane()); } } ObservableList<Node> children = vbox.getChildren(); children.forEach(c -> c.setOnMouseReleased(e -> { if (TotalState.getInstance().getStaminaState().canIncrease()) { stage.close(); } })); } private void loadBathItems() { Map<String, BathItem> bathItemList = ItemWarehouse.getInstance().getBathItemMap(); for (Map.Entry<String, BathItem> entry : bathItemList.entrySet()) { if (entry.getValue().getItemNum() != 0) { vbox.getChildren().add(entry.getValue().toItemAnchorPane()); } } ObservableList<Node> children = vbox.getChildren(); children.forEach(c -> c.setOnMouseReleased(e -> { if (TotalState.getInstance().getCleanlinessState().canIncrease()) { stage.close(); } })); } private void loadDrugItems() { // Xiao Hei won't get sick! (not because I can't find the material QAQ) } }
Load items - take food as an example
The user owns the total warehouse ItemWarehouse and can view all the items he owns. Therefore, he only needs to traverse all the items in the warehouse (even if there are 0 items here, there are such items in the warehouse, but they will not be displayed). If the number of items is greater than or equal to 1, they will be added to vbox to be displayed.
Here, I encapsulate the generation of each element of vbox, that is, anchorPane, in the FoodItem class, which also simplifies the amount of code and improves the reusability of anchorPane (the same object can be used again only by changing the text)
Map<String, FoodItem> foodItemList = ItemWarehouse.getInstance().getFoodItemMap(); for (Map.Entry<String, FoodItem> entry : foodItemList.entrySet()) { if (entry.getValue().getItemNum() != 0) { vbox.getChildren().add(entry.getValue().toItemAnchorPane()); } }
At the same time, add a click event for each item. When you click an item, it means you want to use the commodity (of course, if it can be used), and the window will be closed by default:
ObservableList<Node> children = vbox.getChildren(); children.forEach(c -> c.setOnMouseReleased(e -> { if (TotalState.getInstance().getStaminaState().canIncrease()) { stage.close(); } }));
Item family
Item enumeration class (better choice for global variables)
Take food as an example. Each food should include the following information:
- Name of food
- Food ID
- Picture resource path of food
- How much physical strength can you increase by eating a certain food
- The action picture resource path corresponding to eating this food (in the future, we want to make an action queue so that three or more consecutive actions can be realized without customizing GIF)
package org.taibai.hellohei.items.food; /** * <p>Creation Time: 2021-09-27 17:22:01</p> * <p>Description: Item List, list all food, bath supplies and drugs</p> * * @author Taibai */ public enum FoodEnum { EGG("egg", "FOOD_001", "foods/egg.png", 10, "eat drumstick.gif"), MILK("milk", "FOOD_002", "foods/milk.png", 5, "eat drumstick.gif"); /** * Food name */ private final String name; /** * Unique ID of food */ private final String id; /** * Picture resource path of food */ private final String path; /** * How much satiety can you increase by eating one of these */ private final int buff; /** * Picture resource path for eating */ private final String actionPath; public static final String pathPrefix = "/org/taibai/hellohei/img/"; FoodEnum(String name, String id, String path, int buff, String actionPath) { this.name = name; this.id = id; this.path = path; this.buff = buff; this.actionPath = actionPath; } public String getName() { return name; } public String getId() { return id; } public String getPath() { return pathPrefix + path; } public int getBuff() { return buff; } public String getActionPath() { return pathPrefix + actionPath; } }
Item entity class
An item should have the following two information:
- What is the item: foodEnum
- How many items are there: itemNum
In addition, I reuse the items in the pane of the item display menu, so that I can reuse the original pane without rebuilding the AnchorPane by modifying the label.
package org.taibai.hellohei.items.food; import javafx.geometry.Pos; import javafx.scene.control.Label; import javafx.scene.image.ImageView; import javafx.scene.layout.AnchorPane; import org.taibai.hellohei.constant.Constant; import org.taibai.hellohei.img.ResourceGetter; import org.taibai.hellohei.state.TotalState; import org.taibai.hellohei.ui.Action; import org.taibai.hellohei.ui.ActionExecutor; import org.taibai.hellohei.ui.InterfaceFunction; /** * <p>Creation Time: 2021-09-27 17:09:50</p> * <p>Description: Articles for eating, bathing and seeing a doctor</p> * * @author Taibai */ public class FoodItem { private FoodEnum foodEnum; private int itemNum; private AnchorPane anchorPane; private Label label; public FoodItem(FoodEnum foodEnum, int itemNum) { this.foodEnum = foodEnum; this.itemNum = itemNum; init(); } /** * Create an AnchorPane to display in ItemsWindow, and add a click event at the same time * Click to generate the action of using the item and subtract an item * * @return Created AnchorPane */ public AnchorPane toItemAnchorPane() { label.setText(foodEnum.getName() + "*" + itemNum); return anchorPane; } /** * Only the number of objects changes each time, so there is no need to create new objects again, otherwise it will be particularly time-consuming */ private void init() { anchorPane = new AnchorPane(); ImageView imageView = new ImageView(ResourceGetter.getInstance().get(foodEnum.getPath())); imageView.setFitWidth(86); imageView.setFitHeight(86); label = new Label(foodEnum.getName() + "*" + itemNum); label.setLayoutX(0); label.setLayoutY(66); label.setMinWidth(86); label.setMinHeight(20); label.setAlignment(Pos.CENTER); //Vertically and horizontally centered label.setStyle("-fx-background-color: rgba(0, 0, 0, 0.6); -fx-text-fill: white"); anchorPane.getChildren().addAll(imageView, label); anchorPane.setOnMousePressed(e -> { useItem(); }); } private void useItem() { if (itemNum <= 0) return; if (!TotalState.getInstance().getStaminaState().canIncrease()) { InterfaceFunction.getInstance().say("You can't eat any more~", Constant.UserInterface.SayingRunTime); return; } decrease(1); Action action = Action.creatTemporaryInterruptableAction( foodEnum.getActionPath(), Constant.UserInterface.ActionRunTime * 2, Constant.ImageShow.mainImage ); ActionExecutor.getInstance().execute(action); InterfaceFunction.getInstance().say("Really delicious", Constant.UserInterface.SayingRunTime); // Increase physical strength TotalState.getInstance().getStaminaState().increase(foodEnum.getBuff()); } /** * Increase the number of item s by num * * @param num Increased quantity */ public void increase(int num) { this.itemNum += num; } /** * Reduce the number of item s by num * * @param num Reduced quantity */ public void decrease(int num) { this.itemNum -= num; itemNum = Math.max(0, itemNum); } /** * How many more items are there to get this item * * @return item Number of */ public int getItemNum() { return itemNum; } }
Warehouse of items (globally unique, single instance mode)
Of course, it's simple to write a dead warehouse for a single machine. There are 10 items by default.
In the future, you can access the back-end and synchronize the data locally after login. This is why you need to set the item ID to uniquely identify the item.
package org.taibai.hellohei.items; import org.taibai.hellohei.items.bath.BathEnum; import org.taibai.hellohei.items.bath.BathItem; import org.taibai.hellohei.items.food.FoodEnum; import org.taibai.hellohei.items.food.FoodItem; import java.util.HashMap; import java.util.Map; /** * <p>Creation Time: 2021-09-27 17:35:14</p> * <p>Description: Item Stock</p> * * @author Taibai */ public class ItemWarehouse { private static ItemWarehouse itemWarehouse; private final Map<String, FoodItem> foodItemMap = new HashMap<>(); private final Map<String, BathItem> bathItemMap = new HashMap<>(); private ItemWarehouse() { // There are 10 by default, which can be persisted after the backend system is written for (FoodEnum foodEnum : FoodEnum.values()) { foodItemMap.put(foodEnum.getId(), new FoodItem(foodEnum, 10)); } // There are also 10 bath supplies by default for (BathEnum bathEnum : BathEnum.values()) { bathItemMap.put(bathEnum.getId(), new BathItem(bathEnum, 10)); } } public static ItemWarehouse getInstance() { if (itemWarehouse == null) itemWarehouse = new ItemWarehouse(); return itemWarehouse; } public Map<String, FoodItem> getFoodItemMap() { return foodItemMap; } public Map<String, BathItem> getBathItemMap() { return bathItemMap; } }
State Family
Mood value status class
The mood value is that clicking Xiaohei can make Xiaohei happy, and there must be an expression of happiness, right? It is not only the change of internal data, but also the rising text expression (cloud is used here instead, and no good-looking material can be found for the time being)
package org.taibai.hellohei.state; import javafx.animation.*; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.util.Duration; import org.taibai.hellohei.constant.Constant; import org.taibai.hellohei.img.ResourceGetter; /** * <p>Creation Time: 2021-09-25 02:59:11</p> * <p>Description: Xiaohei's mood value</p> * * @author Taibai */ public class EmotionState { /** * Mood value, value is [0, 100] */ private int emotion = 60; public static final int Reduce_Step = 5; public static final int Increase_Step = 10; public static final int Max_Value = 100; public static final int Min_Value = 0; private ImageView imageView; private final ResourceGetter resourceGetter = ResourceGetter.getInstance(); public EmotionState() { imageView = new ImageView(); } /** * Mood value decreased */ public void reduce() { emotion = Math.max(Min_Value, emotion - Reduce_Step); } /** * Mood value increases */ public void increase() { if (emotion < Max_Value) showIncreasedAnimation(); emotion = Math.min(Max_Value, emotion + Increase_Step); System.out.printf("[EmotionState::increase]-Current mood=%d\n", emotion); } /** * Animation showing increased mood */ private void showIncreasedAnimation() { Image increasingImg = resourceGetter.get(Constant.ImageShow.emotionIncreasingImage); imageView.setImage(increasingImg); imageView.setStyle("-fx-background:transparent;"); // Sets the position relative to the parent container imageView.setX(0); imageView.setY(0); imageView.setLayoutX(60); imageView.setLayoutY(0); imageView.setFitHeight(80); // Set the size of the picture display imageView.setFitHeight(80); imageView.setPreserveRatio(true); // Keep width:height scale imageView.setVisible(true); double millis = Constant.UserInterface.ActionRunTime * 1000; // Displacement animation TranslateTransition translateTransition = new TranslateTransition(Duration.millis(millis), imageView); translateTransition.setInterpolator(Interpolator.EASE_BOTH); translateTransition.setFromY(40); translateTransition.setToY(0); // translateTransition.play(); // Fade in and fade out animation FadeTransition fadeTransition = new FadeTransition(Duration.millis(millis), imageView); fadeTransition.setFromValue(1.0); fadeTransition.setToValue(0); // Parallel execution animation ParallelTransition parallelTransition = new ParallelTransition(); parallelTransition.getChildren().addAll( fadeTransition, translateTransition ); parallelTransition.play(); } public ImageView getImageView() { return imageView; } }
Basically, let a picture show, and add two animations: move up, fade in and fade out
Let the two animations be displayed in parallel to get the effect in the figure.
Physical strength status class
In fact, I don't want to call it physical strength. I used to call it "hunger", but recently, when I think about it, eating will lead to the decline of "hunger". Although it is logical, it is still a little illogical? But what about "satiety" and "fullness"?
package org.taibai.hellohei.state; /** * <p>Creation Time: 2021-09-28 00:51:27</p> * <p>Description: Physical strength value</p> * * @author Taibai */ public class StaminaState { private int stamina = 60; public static final int Reduce_Step = 2; public static final int Max_Value = 100; public static final int Min_Value = 0; /** * Reduced physical strength */ public void reduce() { stamina = Math.max(Min_Value, stamina - Reduce_Step); } /** * Physical strength increased * * @param num Increased amount */ public void increase(int num) { stamina = Math.min(Max_Value, stamina + num); System.out.printf("[StaminaState::increase(%d)]-Current physical strength=%d\n", num, stamina); } /** * Can it be increased * * @return Can increase */ public boolean canIncrease() { return stamina < Max_Value; } }
Total status (globally unique, singleton mode)
After that, the obtained states are obtained from the total state, so setting the singleton mode can also ensure that the sub states are unique.
package org.taibai.hellohei.state; import javax.swing.text.html.ImageView; /** * <p>Creation Time: 2021-09-25 02:58:15</p> * <p>Description: All States of Xiaohei</p> * * @author Taibai */ public class TotalState { private static TotalState totalState; private final EmotionState emotionState; private final StaminaState staminaState; private final CleanlinessState cleanlinessState; private TotalState() { emotionState = new EmotionState(); staminaState = new StaminaState(); cleanlinessState = new CleanlinessState(); } public static TotalState getInstance() { if (totalState == null) totalState = new TotalState(); return totalState; } public EmotionState getEmotionState() { return emotionState; } public StaminaState getStaminaState() { return staminaState; } public CleanlinessState getCleanlinessState() { return cleanlinessState; } }
A little optimization
Before writing the code, we didn't think carefully. Setting the globally shared object into the singleton mode will really save a lot of things, so we don't have to pass the object around when constructing the object.
Here, the ImageView displaying GIF and the Stage displaying ImageView are set to the singleton mode. I call it MainNode, which means the main node
package org.taibai.hellohei.ui; import javafx.scene.image.ImageView; import javafx.stage.Stage; /** * <p>Creation Time: 2021-09-27 22:37:00</p> * <p>Description: Action display window, including ImageView (display GIF) and stage (control the display, hiding, closing, etc. of the whole program) of the main interface * After all, it is a globally unique object, so if it is set to singleton mode, the globally unique object will be obtained</p> * * @author Taibai */ public class MainNode { private static MainNode mainNode; private final ImageView imageView; private final Stage stage; private MainNode() { imageView = new ImageView(); stage = new Stage(); } public static MainNode getInstance() { if (mainNode == null) mainNode = new MainNode(); return mainNode; } public ImageView getImageView() { return imageView; } public Stage getStage() { return stage; } }
In addition, all previous objects using the ImageView/Stage are modified again to ensure that the same object is obtained globally.
Later words
After October, things began to change, competitions began to increase, and the soft test for registration should come as promised. I hope to continue to promote the development of the project in my spare time
Project GitHub warehouse address = > Jiang-TaiBai/IXiaoHei <=
National Day is coming. I wish you a happy National Day~