大家下午好。我正在嘗試將控制器啟動到 FXML 頁面,但不確定如何。我目前正在使用 FXRouter GitHub API。
我想將控制器頁面系結到 FXML 檔案之外的 FXML 頁面。我在下面有一個“LoadNewRoute”方法,目前正在這樣做,但我想改進這一點,因此它將控制器分配給外部的 FXML 頁面。做這個的最好方式是什么?
加載頁面的方法
private static void loadNewRoute(RouteScene route) throws IOException {
// get Main Class package name to get correct files path
String pathRef = mainRef.getClass().getPackage().getName();
// set FXRouter current route reference
currentRoute = route;
// create correct file path. "/" doesn't affect any OS
String scenePath = "/" pathRef "/" route.scenePath;
// load .fxml resource
FXMLLoader loader = new FXMLLoader(new Object() { }.getClass().getResource(scenePath));
Parent resource = loader.load();
// resource.setController(BuyController);
// set window title from route settings or default setting
window.setTitle(route.windowTitle);
// set new route scene
window.setScene(new Scene(resource, route.sceneWidth, route.sceneHeight));
// show the window
window.show();
// set scene animation
routeAnimation(resource);
}
FXML 頁面-
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.CheckBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.Menu?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.MenuItem?>
<?import javafx.scene.control.RadioButton?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.text.Font?>
<AnchorPane xmlns="http://javafx.com/javafx/17" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.gradle.Controllers.BuyController" >
<MenuBar layoutX="676.0" layoutY="2.0">
<Menu mnemonicParsing="false" text="Pages">
<MenuItem mnemonicParsing="false" onAction="#SwitchToMainScene" text="Home" />
<MenuItem mnemonicParsing="false" onAction="#SwitchToBuyScene" text="Buy" />
<MenuItem mnemonicParsing="false" onAction="#SwitchToSellScene" text="Sell" />
</Menu>
<Menu mnemonicParsing="false" text="Options">
<MenuItem mnemonicParsing="false" onAction="#CloseApp" text="Exit" />
<MenuItem mnemonicParsing="false" onAction="#SwitchToProfileScene" text="Profile" />
</Menu>
</MenuBar>
<Pane layoutX="14.0" layoutY="67.0" prefHeight="200.0" prefWidth="200.0">
<CheckBox layoutX="7.0" layoutY="116.0" mnemonicParsing="false" prefHeight="17.0" prefWidth="82.0" text="DogeCoin" />
<CheckBox layoutX="7.0" layoutY="153.0" mnemonicParsing="false" prefHeight="17.0" prefWidth="72.0" text="Bitcoin" />
<CheckBox layoutX="7.0" layoutY="83.0" mnemonicParsing="false" text="Etherium" />
<Label layoutX="27.0" layoutY="26.0" text="Select your CryptoCurrency" />
</Pane>
<Pane layoutX="528.0" layoutY="-1.0" prefHeight="37.0" prefWidth="272.0">
<Button layoutX="222.0" layoutY="-1.0" mnemonicParsing="false" onAction="#CloseApp" prefHeight="30.0" prefWidth="50.0" text="Exit" />
<Button layoutX="166.0" layoutY="-1.0" mnemonicParsing="false" onAction="#SwitchToProfileScene" prefHeight="30.0" prefWidth="56.0" text="Profile" />
<Button layoutX="118.0" layoutY="-1.0" mnemonicParsing="false" onAction="#SwitchToSellScene" prefHeight="30.0" prefWidth="48.0" text="Sell" />
<Button layoutX="70.0" layoutY="-1.0" mnemonicParsing="false" onAction="#SwitchToBuyScene" prefHeight="30.0" prefWidth="48.0" text="Buy" />
<Button layoutX="14.0" layoutY="-1.0" mnemonicParsing="false" onAction="#SwitchToMainScene" prefHeight="30.0" prefWidth="56.0" text="Home" />
</Pane>
<Pane layoutX="300.0" layoutY="88.0" prefHeight="200.0" prefWidth="200.0">
<RadioButton layoutX="13.0" layoutY="38.0" mnemonicParsing="false" text="Amount in coin" />
<RadioButton layoutX="13.0" layoutY="67.0" mnemonicParsing="false" text="Amount in cash" />
<TextField layoutX="13.0" layoutY="139.0" />
<Label layoutX="-1.0" prefHeight="34.0" prefWidth="200.0" text="Choose type of buying, and enter amount" wrapText="true" />
</Pane>
<Button layoutY="-1.0" mnemonicParsing="false" onAction="#BackButton" prefHeight="30.0" prefWidth="64.0" text="Back" />
<Button layoutX="237.0" layoutY="447.0" mnemonicParsing="false" onAction="#BuyButton" prefHeight="82.0" prefWidth="278.0" text="Buy">
<font>
<Font size="31.0" />
</font>
</Button>
</AnchorPane>
控制器-
package com.gradle.Controllers;
import com.gradle.Controller;
import com.gradle.FXRouter;
import javafx.event.ActionEvent;
import javafx.fxml.FXMLLoader;
import java.io.IOException;
public class BuyController extends com.gradle.Controller {
public void example () {
}
家庭控制器
public class HomeController extends com.gradle.Controller {
public void SwitchToMainScene (ActionEvent event) throws IOException {
FXRouter.goTo("Home", new HomeController());}
public void SwitchToBuyScene (ActionEvent event) throws IOException {
FXRouter.goTo("Buy", new BuyController());}
public void SwitchToSellScene (ActionEvent event) throws IOException {
FXRouter.goTo("Sell", new SellController());}
public void SwitchToProfileScene (ActionEvent event) throws IOException {
FXRouter.goTo("Profile", new ProfileController());}
@Override
public void initilize(URL url, ResourceBundle rb) {
}
}
稱呼-
FXRouter.goTo("Home", new HomeController());
FXML-
<Button fx:id="exit" layoutX="222.0" layoutY="-1.0" mnemonicParsing="false" onAction="#CloseApp" prefHeight="30.0" prefWidth="50.0" text="Exit" />
<Button layoutX="166.0" layoutY="-1.0" mnemonicParsing="false" onAction="#SwitchToProfileScene" prefHeight="30.0" prefWidth="56.0" text="Profile" />
<Button layoutX="118.0" layoutY="-1.0" mnemonicParsing="false" onAction="#SwitchToSellScene" prefHeight="30.0" prefWidth="48.0" text="Sell" />
<Button layoutX="70.0" layoutY="-1.0" mnemonicParsing="false" onAction="#SwitchToBuyScene" prefHeight="30.0" prefWidth="48.0" text="Buy" />
<Button layoutX="14.0" layoutY="-1.0" mnemonicParsing="false" onAction="#SwitchToMainScene" prefHeight="30.0" prefWidth="56.0" text="Home" />
</Pane>
<Button layoutY="-1.0" mnemonicParsing="false" onAction="#BackButton" prefHeight="30.0" prefWidth="64.0" text="Back" />
</AnchorPane>
所有 onAction 都給我錯誤,它找不到呼叫
uj5u.com熱心網友回復:
沒有 FXMLRouter
在這個問題的答案中演示了為 FXML(沒有 FXMLRouter 庫)手動配置控制器的標準方法:
- JavaFX - 如何在 fxml 控制器初始化期間設定值
請注意,如果您手動設定控制器,你應該不還提供了一個fx:controller在FXML檔案屬性。
如果您沒有使用 FXMLRouter 庫,那么只需按照鏈接問題的答案中的示例進行操作,而忽略此答案的其余部分。
使用 FXMLRouter
我看了代碼,FXMLRouter不能做你想做的。
它使用靜態加載功能。如果你想設定一個你創建的控制器,那么你必須使用一個加載器實體,如先前參考的答案中所示。
整個路由器只是一類。
如果你想使用它:
將源代碼復制到您的專案中。
修改loadNewRoute方法(以及呼叫它的方法)以傳入您創建的控制器。
- 如果您想保留當前的公共介面,您可以這樣做并添加新的多載介面,這些介面也接受您本地構造的控制器類。
如果手動設定控制器,請更改實作以創建新的 FXMLLoader 并在新的加載器實體上設定控制器,而不是使用靜態加載方法。
先前參考的答案中演示了對加載實作所需的修改。
或者,您可以修改 FXMLLoader 代碼以使用本答案中其他地方討論的自定義 FXML 控制器工廠方法。
FXMLRouter 實作示例
這不是我的代碼,我已經根據此答案中先前的修改建議對其進行了修改,原始代碼和許可證資訊位于:
- https://github.com/Marcotrombino/FXRouter
我根本沒有測驗過這些修改,也不保證它們會起作用。代碼僅作為示例提供。
import java.io.IOException;
import java.util.AbstractMap;
import java.util.HashMap;
import javafx.fxml.FXMLLoader;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.Parent;
import javafx.util.Duration;
import javafx.animation.FadeTransition;
/**
* FXRouter allows to manage scenes switching on JavaFX Application with an easy API
* Inspired by Angular JS $routeProvider
* @author Marco Trombino
* @version 1.0.0
*/
public final class FXRouter {
private static final String WINDOW_TITLE = "";
private static final Double WINDOW_WIDTH = 800.0;
private static final Double WINDOW_HEIGHT = 600.0;
private static final Double FADE_ANIMATION_DURATION = 800.0;
// FXRouter Singleton
private static FXRouter router;
// FXRouter Main Class reference to get main package
private static Object mainRef;
// FXRouter Application Stage reference to set scenes
private static Stage window;
// Application Stage title
private static String windowTitle;
// Application Stage size
private static Double windowWidth;
private static Double windowHeight;
// routes switching animation
private static String animationType;
private static Double animationDuration;
// FXRouter routes map
private static AbstractMap<String, RouteScene> routes = new HashMap<>();
// FXRouter current route
private static RouteScene currentRoute;
/**
* FXRouter Inner Class used into routes map
*/
private static class RouteScene {
// route .fxml Scene path
private String scenePath;
// Scene (Stage) title
private String windowTitle;
private double sceneWidth;
private double sceneHeight;
// route data passed from goTo()
private Object data;
private RouteScene(String scenePath) {
this(scenePath, getWindowTitle(), getWindowWidth(), getWindowHeight());
}
private RouteScene(String scenePath, String windowTitle) {
this(scenePath, windowTitle, getWindowWidth(), getWindowHeight());
}
private RouteScene(String scenePath, double sceneWidth, double sceneHeight) {
this(scenePath, getWindowTitle(), sceneWidth, sceneHeight);
}
/** Route scene constructor
* @param scenePath: .FXML scene file
* @param windowTitle: Scene (Stage) title
* @param sceneWidth: Scene Width
* @param sceneHeight: Scene Height
*/
private RouteScene(String scenePath, String windowTitle, double sceneWidth, double sceneHeight) {
this.scenePath = scenePath;
this.windowTitle = windowTitle;
this.sceneWidth = sceneWidth;
this.sceneHeight = sceneHeight;
}
private static String getWindowTitle() {
return FXRouter.windowTitle != null ? FXRouter.windowTitle : WINDOW_TITLE;
}
private static double getWindowWidth() {
return FXRouter.windowWidth != null ? FXRouter.windowWidth : WINDOW_WIDTH;
}
private static double getWindowHeight() {
return FXRouter.windowHeight != null ? FXRouter.windowHeight : WINDOW_HEIGHT;
}
}
/**
* FXRouter constructor kept private to apply Singleton pattern
*/
private FXRouter() {}
public static void bind(Object ref, Stage win) {
checkInstances(ref, win);
}
public static void bind(Object ref, Stage win, String winTitle) {
checkInstances(ref, win);
windowTitle = winTitle;
}
public static void bind(Object ref, Stage win, double winWidth, double winHeight) {
checkInstances(ref, win);
windowWidth = winWidth;
windowHeight = winHeight;
}
/** FXRouter binder with Application Stage and main package
* @param ref: Main Class reference
* @param win: Application Stage
* @param winTitle: Application Stage title
* @param winWidth: Application Stage width
* @param winHeight: Application Stage height
*/
public static void bind(Object ref, Stage win, String winTitle, double winWidth, double winHeight) {
checkInstances(ref, win);
windowTitle = winTitle;
windowWidth = winWidth;
windowHeight = winHeight;
}
/** set FXRouter references only if they are not set yet
* @param ref: Main Class reference
* @param win: Application Stage
*/
private static void checkInstances(Object ref, Stage win) {
if(mainRef == null)
mainRef = ref;
if(router == null)
router = new FXRouter();
if(window == null)
window = win;
}
public static void when(String routeLabel, String scenePath) {
RouteScene routeScene = new RouteScene(scenePath);
routes.put(routeLabel, routeScene);
}
public static void when(String routeLabel, String scenePath, String winTitle) {
RouteScene routeScene = new RouteScene(scenePath, winTitle);
routes.put(routeLabel, routeScene);
}
public static void when(String routeLabel, String scenePath, double sceneWidth, double sceneHeight) {
RouteScene routeScene = new RouteScene(scenePath, sceneWidth, sceneHeight);
routes.put(routeLabel, routeScene);
}
/** Define a FXRouter route
* @param routeLabel: Route label identifier
* @param scenePath: .FXML scene file
* @param winTitle: Scene (Stage) title
* @param sceneWidth: Scene Width
* @param sceneHeight: Scene Height
*/
public static void when(String routeLabel, String scenePath, String winTitle, double sceneWidth, double sceneHeight) {
RouteScene routeScene = new RouteScene(scenePath, winTitle, sceneWidth, sceneHeight);
routes.put(routeLabel, routeScene);
}
public static void goTo(String routeLabel) throws IOException {
goTo(routeLabel, null, null);
}
public static void goTo(String routeLabel, Object data) throws IOException {
goTo(routeLabel, data, null);
}
/** Switch between FXRouter route and show corresponding scenes
* @param routeLabel: Route label identifier
* @param data: Data passed to route
* @oaram controller: Controller instance to use.
* @throws Exception: throw FXMLLoader exception if file is not loaded correctly
*/
public static void goTo(String routeLabel, Object data, Object controller) throws IOException {
// get corresponding route
RouteScene route = routes.get(routeLabel);
// set route data
route.data = data;
loadNewRoute(route, controller);
}
/** Helper method of goTo() which load and show new scene
* @throws Exception: throw FXMLLoader exception if file is not loaded correctly
*/
private static void loadNewRoute(RouteScene route, Object controller) throws IOException {
// get Main Class package name to get correct files path
String pathRef = mainRef.getClass().getPackage().getName();
// set FXRouter current route reference
currentRoute = route;
// create correct file path. "/" doesn't affect any OS
String scenePath = "/" pathRef "/" route.scenePath;
// load .fxml resource
FXMLLoader loader = new FXMLLoader();
loader.setController(controller);
Parent resource = loader.load(new Object() { }.getClass().getResource(scenePath));
// set window title from route settings or default setting
window.setTitle(route.windowTitle);
// set new route scene
window.setScene(new Scene(resource, route.sceneWidth, route.sceneHeight));
// show the window
window.show();
// set scene animation
routeAnimation(resource);
}
/* Syntactic sugar for goTo() method when FXRouter get set */
public static void startFrom(String routeLabel) throws Exception {
goTo(routeLabel);
}
public static void startFrom(String routeLabel, Object data) throws Exception {
goTo(routeLabel, data);
}
public static void startFrom(String routeLabel, Object data, Object controller) throws Exception {
goTo(routeLabel, data, controller);
}
/** set FXRouter switching animation
* @param anType: Animation type
*/
public static void setAnimationType(String anType) {
animationType = anType;
}
/** set FXRouter switching animation
* @param anType: Animation type
* @param anDuration: Animation duration
*/
public static void setAnimationType(String anType, double anDuration) {
animationType = anType;
animationDuration = anDuration;
}
/** Animate routes switching based on animation type
*/
private static void routeAnimation(Parent node) {
String anType = animationType != null ? animationType.toLowerCase() : "";
switch(anType) {
case "fade":
Double fd = animationDuration != null ? animationDuration : FADE_ANIMATION_DURATION;
FadeTransition ftCurrent = new FadeTransition(Duration.millis(fd), node);
ftCurrent.setFromValue(0.0);
ftCurrent.setToValue(1.0);
ftCurrent.play();
break;
default:
break;
}
}
/** Get current route data
*/
public static Object getData() {
return currentRoute.data;
}
}
替代實施
如果不需要,請忽略此部分。FXMLRouter 非常簡單,如果修改滿足您的要求,那就太好了。
有可用的替代庫可以完成類似的事情,我不會在這里推薦任何庫,但我建議您通過 eden 編碼查看本教程:
- https://edencoding.com/dependency-injection/
伊甸園教程中有一個框,標題為“依賴注入器-完整代碼”,看完教程后,展開框,研究代碼,看能不能看懂,能看懂就說明高級用法FXMLLoader 好,如果沒有,那也行。
You don't need to use the concepts from it if you don't want to or if what you currently have already suits your purposes. I just provide the tutorial link as a reference as it helps explain some complex topics quite well.
More sophisticated again is to integrate JavaFX with SpringBoot, but that is more complex, so I won't recommend that here unless it was something you really wanted to do to take advantage of other features of SpringBoot.
FAQ
Do you think it would be possible to take out the need for a Controller to be passed, if you used a switch?
If you look at the example code I provided, I already did that.
A switch is not needed, you can just use overloaded methods (which is the way I did it).
If you don't want to pass the controller, then don't use the method which passes the controller.
For example, if you don't want to pass a controller, call goTo(routeLabel) or goTo(routeLabel, data) rather than goTo(routeLabel, data, controller).
The new code is backward compatible with the existing implementation, so if you don't add the extra parameter in the call, no controller will be set (just like how the original code worked).
But if you don't pass a controller, you should specify the controller in the FXML in an fx:controller attribute.
How would you then create the object from the controller classes? > I currently have
public void SwitchToMainScene (ActionEvent event) throws IOException {FXRouter.goTo("Home");}Would I just add in my BuyController name at the end?
There are two ways you can do this.
The simplest way would be to manually create and pass the controller for each call:
public void switchToMainScene(ActionEvent event) throws IOException { FXRouter.goTo( "Home", null, new BuyController( <whatever parameters you want to pass to your new instance> ) ); }
Note: I changed the case for the method example, always write code that follows the Java naming conventions. If you don't want to pass any parameters to the controller constructor, then don't, in such a case the behavior will be the same as if the FXMLLoader constructed the controller from the fx:controller attribute in the FXML.
The alternate method is to use a controller factory. This is discussed in the eden coding dependency injection tutorial I referred you to previously.
To use a factory with the FXMLRouter, modify the FXMLRouter implementation to:
- Create a factory that maps your FXML files to controller instance (either via a stored hash map or a switch statement or using a dependency injection framework).
- Set the factory on the FXMLRouter instance (could be statically set for the entire application if you wanted).
- Set the controller factory on the loader instance rather than setting the controller instance on the loader instance.
I won't provide code for this here at this time, perhaps you could work out what to do from the Javadoc, the eden tutorial, and the prior example for setting the constructor manually.
Whenever I remove the fx:Controller from my .fxml, all of my onAction refuses to work, even though I have them assigned in the controller.
When you remove the fx:controller reference, the Idea IDE can't work out what controller is associated with an FXML file, and will highlight errors for references to methods it can't resolve for intelligent editing and navigation.
但是,在運行時,控制器已設定并決議,方法參考也是如此(只要您正確命名了它們,不要忘記名稱區分大小寫),因此這不是實際錯誤,您可以忽略它,盡管您確實失去了 Idea 智能編輯的一些好處。
或者,為了允許您fx:controller在 fxml 中指定一個值(保留智能編輯)并手動配置控制器,您可以使用控制器工廠。答案中有一個示例實作:
- 如何告訴 IntelliJ 從 FXMLLoader 獲取 FXML 控制器以進行語法高亮顯示?
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/398129.html
