访问FXML控制器类


73

我想随时与FXML控制器类进行通信,以从主应用程序或其他阶段更新屏幕上的信息。

这可能吗?我还没有找到任何办法。

静态函数可能是一种方法,但是它们无法访问表单的控件。

有任何想法吗?

Answers:


111

您可以从 FXMLLoader

FXMLLoader fxmlLoader = new FXMLLoader();
Pane p = fxmlLoader.load(getClass().getResource("foo.fxml").openStream());
FooController fooController = (FooController) fxmlLoader.getController();

将其存储在您的主要阶段,并提供getFooController()getter方法。
在其他类或阶段中,每当您需要刷新加载的“ foo.fxml”页面时,请从其控制器中进行询问:

getFooController().updatePage(strData);

updatePage()可能类似于:

// ...
@FXML private Label lblData;
// ...
public void updatePage(String data){
    lblData.setText(data);
}
// ...

在FooController类中。
这样,其他页面用户就不必担心页面的内部结构(例如,内容和位置Label lblData)。

还要查看https://stackoverflow.com/a/10718683/682495。在JavaFX 2.2FXMLLoader中进行了改进。


2
我发现使用静态函数比该解决方案更容易且效果更好(至少对我而言)。能够访问控件的关键是拥有一个可以访问所有控件和公共方法的类的公共静态实例。
betaman 2012年

我知道这是一个古老的问题...但是任何人都可以在答案wrt的前三行代码以及getFooController()的位置上提供更多详细信息吗?
阿德纳2014年

@ adeena,FooController是您在“ foo.fxml”中定义的控制器类,并且代码中没有getFooController()。
Uluk Biy 2014年

@UlukBiy嗯...也许我正在尝试做一些与原始海报不同的事情。在主要阶段,我有一个FXML应用程序和一个TextArea。我想出了如何从其他FXML控制器而不是从我的包/项目中的任何其他类型的类访问它。
阿德纳2014年

1
@TannerSummers,一旦获得了控制器实例,就需要将其放到某个常见的地方(像回购一样考虑),以便应用程序的其他类(模块)可以访问它。“主阶段”是指具有JavaFX应用程序入口点的类,即具有start(Stage stage)方法的类。在该主类中,您可能希望将控制器实例存储在某个集合中。或创建一个新的ControllerManager类,该类充当用于存储和访问它们的存储库。设计由您决定。
Uluk Biy 2015年

26

只是为了帮助阐明可接受的答案,并可能为JavaFX的其他新用户节省一些时间:

对于JavaFX FXML应用程序,NetBeans将在主类中自动生成您的start方法,如下所示:

@Override
public void start(Stage stage) throws Exception {
    Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));

    Scene scene = new Scene(root);

    stage.setScene(scene);
    stage.show();
}

现在,要访问控制器类,我们要做的就是将FXMLLoaderload()方法从静态实现更改为实例化实现,然后我们可以使用实例的方法来获取控制器,如下所示:

//Static global variable for the controller (where MyController is the name of your controller class
static MyController myControllerHandle;

@Override
public void start(Stage stage) throws Exception {
    //Set up instance instead of using static load() method
    FXMLLoader loader = new FXMLLoader(getClass().getResource("FXMLDocument.fxml"));
    Parent root = loader.load();

    //Now we have access to getController() through the instance... don't forget the type cast
    myControllerHandle = (MyController)loader.getController();

    Scene scene = new Scene(root);

    stage.setScene(scene);
    stage.show();
}

8

另一个解决方案是从您的控制器类设置控制器,就像这样...

public class Controller implements javafx.fxml.Initializable {

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        // Implementing the Initializable interface means that this method
        // will be called when the controller instance is created
        App.setController(this);
    }

}

这是我更喜欢使用的解决方案,因为代码有些混乱,无法创建功能齐全的FXMLLoader实例,该实例可以正确处理本地资源等。

@Override
public void start(Stage stage) throws Exception {
    Parent root = FXMLLoader.load(getClass().getResource("/sample.fxml"));
}

@Override
public void start(Stage stage) throws Exception {
    URL location = getClass().getResource("/sample.fxml");
    FXMLLoader loader = createFXMLLoader(location);
    Parent root = loader.load(location.openStream());
}

public FXMLLoader createFXMLLoader(URL location) {
    return new FXMLLoader(location, null, new JavaFXBuilderFactory(), null, Charset.forName(FXMLLoader.DEFAULT_CHARSET_NAME));
}

该解决方案也适用于JavaFX的早期版本(<2.2)。
尼尔

总的来说,我认为选择的答案必须是首选,但要巧妙,尽管这是:首先,您可以有多个控制器,所以您真的想在Application类中有一个静态映射,即关键控制器->值加载器(注意,可以始终从加载程序中获取控制器)。其次,我们得到了一种getController()方法,因此使用它很有意义。第三,static当真正涉及实例操作时,应避免使用方法,并应避免将其作为一般的编码偏好。
mike啮齿动物

将控制器存储在静态字段中不是一个好主意。
Lii

2

在从“主”屏幕加载对象的过程中,传递我已经找到并起作用的数据的一种方法是使用查找,然后将数据设置在不可见的标签内,我以后可以从控制器类中检索该标签。像这样:

Parent root = FXMLLoader.load(me.getClass().getResource("Form.fxml"));
Label lblData = (Label) root.lookup("#lblData");
if (lblData!=null) lblData.setText(strData); 

这可行,但是必须有更好的方法。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.