如何在JavaFX中从SceneBuilder访问UI组件

6

(重复并解决-请参见下面的答案)

我正在学习JavaFX,使用“SceneBuilder”似乎相当困难。我习惯于Android和QtCreator。在我看来,访问UI组件要容易得多。

类似于findViewById(R.id.btnPushMe); <- Android 代码

实际上,我已经找到了一种解决方案,但使用起来相当不舒服。如下所示:

FXMLLoader loader = new FXMLLoader(MainApp.class.getResource("../fmxl/main.fxml"));

AnchorPane pane = loader.load();
System.out.println("panechilds:" + pane.getChildren().size());

BorderPane border = (BorderPane) pane.getChildren().get(0);
System.out.println("borderchilds:" + border.getChildren().size());

the xml..

<AnchorPane fx:id="mAnchor" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0"
            xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.progui.MainController">
    <children>
        <BorderPane layoutX="-1.0" prefHeight="600.0" prefWidth="800.0">
            <top>

               ...

提前感谢您的帮助,Martin。

编辑:

这是一个重复的问题(但我不会删除它,因为我花了一些时间找到答案 - 也许是因为JavaFX没有像Android问题那样被问到..)

AnchorPane anchor = (AnchorPane) scene.lookup("#mAnchor");

在这里找到:如何在JavaFX中查找带ID的元素?


使用FXML,您将在控制器中定义实例字段,用于您感兴趣的UI元素。这些字段在加载控制器类实例期间初始化,将包含对您希望使用的GUI对象的引用。 - scottb
是的,我经常看到这样的写法。但我并没有看到这种“编码风格”的任何优势。像这样编写的代码对我来说看起来相当令人困惑。。 :-/ 也许我错了,其他程序员会说这很好结构化,但我不太满意使用SceneBuilder来调用方法等。 - Martin Pfeffer
“lookup”方法非常不稳定。只有在CSS应用后才能使用查找,这通常是在节点显示之后(有时是一帧之后)。请使用控制器方法,或者最坏的情况下使用“FXMLLoader.getNamespace()”。 - James_D
2个回答

6
你应该使用一个控制器类并在其中访问UI元素。
基本上,你需要这样做:
<AnchorPane fx:id="mAnchor" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="600.0" prefWidth="800.0"
            xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="app.progui.MainController">
    <children>
        <BorderPane fx:id="border" layoutX="-1.0" prefHeight="600.0" prefWidth="800.0">
            <top>

               ...

然后您可以使用fx:id属性在控制器中访问相关元素

package app.progui ;

// ...

public class MainController {

    @FXML
    private BorderPane border ;


    public void initialize() {
        border.setStyle("-fx-background-color: antiquewhite;");
        // ...
    }

    // ...
}

控制器类中的字段名必须与FXML文件中的fx:id值匹配。
可以在调用FXMLLoader的类中访问带有fx:id属性的元素,但如果需要这样做,则通常意味着整体设计有问题。您可以执行以下操作:
FXMLLoader loader = new FXMLLoader(MainApp.class.getResource("../fmxl/main.fxml"));

AnchorPane pane = loader.load();
Map<String, Object> fxmlNamespace = loader.getNamespace();
BorderPane border = (BorderPane) fxmlNamespace.get("border");

假设在上述FXML片段中定义了fx:id

4
当你设计FXML时,通常需要设计三个东西:应用逻辑、GUI控制器逻辑和FXML。
在加载和初始化控制器类时,FXML加载器将注入您希望访问的UI控件的引用,因此您不需要使用FindById()方法。
控制器类看起来类似于这样:
class DCServRecEditor extends DialogController {


@FXML // ResourceBundle that was given to the FXMLLoader
private ResourceBundle resources;

@FXML // URL location of the FXML file that was given to the FXMLLoader
private URL location;

@FXML // fx:id="ancMatchSelector"
private AnchorPane ancMatchSelector; // Value injected by FXMLLoader

@FXML // fx:id="ancServEditor"
private AnchorPane ancServEditor; // Value injected by FXMLLoader

@FXML // fx:id="ancServRecEditor"
private AnchorPane ancServRecEditor; // Value injected by FXMLLoader
:
:

FXML加载功能会自动将带有@FXML注解的实例字段引用注入其中。要操作UI控件,只需使用适当的引用访问其方法即可。
将UI控件逻辑与应用程序逻辑分离非常理想,它影响了"关注点分离"。当您习惯以这种方式设计FXML UI时,您会喜欢它。

我知道... @FXML 使字段可访问,因此它不需要是公共的。但是,如果我设置 initialize() 方法,则会被标记为未使用(在 IntelliJ 中)- 这正常吗? 您知道哪里有好的教程可以获取有关“生命周期”或更好地说“如何通过 JavaFX 传递对象”的信息吗?我真的感到不熟悉。 - Martin Pfeffer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接