FXML:未指定控制器,而在控制器中指定了控制器

  • 本文关键字:控制器 未指定 FXML java javafx
  • 更新时间 :
  • 英文 :


我正试图在我正在制作的应用程序中使用多个fxml文件,在进行一些研究时,我发现对fxml文件使用自定义控制器是实现此类应用程序的最佳方法。

我遵循了Oracle Docs关于"掌握FXML"的教程,并在CustomController.java文件中将根和控制器设置为"this"。

当intellij发现在fxml文件中没有为onAction处理程序指定控制器时,问题就出现了,而我正在以编程方式指定控制器。

tester.java

package task01;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class tester extends Application
{
@Override
public void start(Stage stage) throws Exception
{
CustomController customController = new CustomController();
customController.getStylesheets().add("/task01/stylize.css");
stage.setScene(new Scene(customController,1920,1080));
stage.setTitle("Seneca ATM Program");
stage.setWidth(1920);
stage.setHeight(1080);
stage.show();
}
public static void main(String[] args)
{
Application.launch(args);
}
}

CustomContoller.java

package task01;
import javafx.event.ActionEvent;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
import javafx.fxml.*;
import javafx.scene.layout.Pane;
import java.io.IOException;
public class CustomController extends GridPane
{
@FXML
private Pane viewableContent;
@FXML
private Button vigilanteButton;
public CustomController()
{
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("root.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try
{
fxmlLoader.load();
} catch (IOException exception)
{
throw new RuntimeException(exception);
}
}
@FXML
private void vigilanteAction(ActionEvent actionEvent)
{
System.out.println("Hello, World");
}
}

root.fxml

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.GridPane?>
<?import task01.MainMenuController?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.Pane?>
<fx:root type="javafx.scene.layout.GridPane" xmlns:fx="http://javafx.com/fxml" alignment="CENTER">
<ImageView fitWidth="229.67" fitHeight="149.67" GridPane.columnIndex="0" GridPane.rowIndex="0" GridPane.halignment="CENTER">
<Image url="/task01/logo.png"/>
</ImageView>
<Pane fx:id="viewableContent" GridPane.columnIndex="0" GridPane.rowIndex="1" GridPane.halignment="CENTER">
<MainMenuController/>
</Pane>
<Button fx:id="vigilanteButton">Vigilante</Button>
</fx:root>

MainMenuController.java

package task01;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import java.io.IOException;
public class MainMenuController extends GridPane
{
private CustomController customController = new CustomController();
public MainMenuController()
{
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("mainmenu.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try
{
fxmlLoader.load();
} catch (IOException exception)
{
throw new RuntimeException(exception);
}
}
@FXML
private VBox buttonSet;
@FXML
private HBox buttonSetOne;
@FXML
private HBox buttonSetTwo;
@FXML
private  Button changePinButton;
@FXML
private Button accountInquiryButton;
@FXML
private Button withdrawMoneyButton;
@FXML
private Button depositMoneyButton;
@FXML
private Button balanceInquiryButton;
@FXML
private Button createAccountButton;
@FXML
private GridPane gridpane;
@FXML
public void createAccountAction(ActionEvent actionEvent)
{
}
}

主菜单.fxml

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.geometry.Insets?>
<GridPane fx:id="gridpane" alignment="CENTER" vgap="50" hgap="50" xmlns:fx="http://javafx.com/fxml">
<padding><Insets top="10" bottom="10" left="10" right="10"/></padding>
<VBox fx:id="buttonSet" spacing="25" GridPane.columnIndex="0" GridPane.rowIndex="1">
<HBox fx:id="buttonSetOne" spacing="25">
<Button styleClass="menuButton" fx:id="createAccountButton" onAction="#createAccountAction">Create account</Button>
<Button styleClass="menuButton" fx:id="changePinButton">Change PIN</Button>
<Button styleClass="menuButton" fx:id="accountInquiryButton">Account Inquiry</Button>
</HBox>
<HBox fx:id="buttonSetTwo" spacing="25">
<Button styleClass="menuButton" fx:id="withdrawMoneyButton">Withdraw Money</Button>
<Button styleClass="menuButton" fx:id="depositMoneyButton">Deposit Money</Button>
<Button styleClass="menuButton" fx:id="balanceInquiryButton">Balance Inquiry</Button>
</HBox>
</VBox>
</GridPane>

问题是,您可以从控制器将FXML文件绑定到控制器,但当您这样做时,IDE直到它启动并运行时才知道它。这就是IDE给您带来麻烦的原因。如果你想设置onAction处理程序,你必须从控制器中进行设置。您必须创建这样的方法,并将onAction侦听器添加到按钮:

@FXML
public void initialize() {
createAccountButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
// TODO Auto-generated method stub
createAccountAction(event);
}
});
}

分而治之:将复杂的问题分解为更小的问题。从下面这样的简单设置开始。

import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class tester extends Application
{
@Override
public void start(Stage stage) throws Exception
{
CustomController customController = new CustomController();
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("root.fxml"));
fxmlLoader.setController(customController);
Parent root = (Parent)fxmlLoader.load();
stage.setScene(new Scene(root,1920,1080));
stage.setTitle("Seneca ATM Program");
stage.show();
}
public static void main(String[] args)
{
Application.launch(args);
}
}
//controller should be just that. It is not a pane 
class CustomController /* extends GridPane */
{
@FXML
private Pane viewableContent;
@FXML
private Button vigilanteButton;
@FXML
private void vigilanteAction(ActionEvent actionEvent)
{
System.out.println("Hello, World");
}
}

root.fxml:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.GridPane?>
<!--  ?import task01.MainMenuController?-->
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.Pane?>
<GridPane xmlns:fx="http://javafx.com/fxml" alignment="CENTER">
<ImageView fitWidth="229.67" fitHeight="149.67" GridPane.columnIndex="0" GridPane.rowIndex="0"
GridPane.halignment="CENTER">
<Image url="task01/logo.png"/>  <!-- todo: set correct path -->
</ImageView>
<Button fx:id="vigilanteButton" onAction="#vigilanteAction">Vigilante</Button>
</GridPane>

我希望这能帮助您构建应用程序。

IDE无法知道您在java代码中的某个地方自己设置了控制器。由于这个原因,自动完成功能等被关闭。IDE显示这类"问题"的事实在这种情况下是不幸的,但它们可能表明只有在运行时才能识别的问题。

在您的情况下,假设您的fxml中没有任何拼写错误,可以安全地忽略这些警告。

当然,您可以在编辑fxml时临时添加fx:controller属性,并在完成后将其删除。

处理此问题的另一种方法是在fxml中指定fx:controller属性,并为加载器设置controllerFactory属性而不是controller属性。不过我真的不喜欢这种方法:

<GridPane fx:id="gridpane" alignment="CENTER" vgap="50" hgap="50" xmlns:fx="http://javafx.com/fxml" fx:controller="task01.MainMenuController">
...
public MainMenuController() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("mainmenu.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setControllerFactory(clazz -> {
if (!clazz.isInstance(this)) {
throw new IllegalArgumentException(String.format("class of controller (%s) not assignable to class specified in fxml (%s)", this.getClass().getName(), clazz.getName()));
}
return this;
});
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}

如果不想检查fxml中指定的控制器类型与指定的控制器的类型之间的分配兼容性,则可以将controllerFactory简化为clazz -> this

如果你想对多个类使用这种方法,我强烈建议你为控制器工厂创建一个类,以避免重复,假设你想检查。。。

最新更新