JavaFX - 将 FXML 包装到 Java 类控制器

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15439894/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 19:35:45  来源:igfitidea点击:

JavaFX - Wrapping FXML to Java Class Controller

javatreeviewjavafxfxml

提问by Leonardo

I have a FXML file that has a TreeView control:

我有一个带有 TreeView 控件的 FXML 文件:

<AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="500.0" xmlns:fx="http://javafx.com/fxml" fx:controller="test.MyControllerClass">

<TreeView fx:id="locationTreeView" layoutX="12.0" layoutY="158.0" prefHeight="193.0" prefWidth="471.0" />

Then my Java Class Controller needs to wrap with this TreeView and add TreeItem's dynamically. That's the problem, it isn't loading those TreeItem's. That's the test code below, from my Controller:

然后我的 Java 类控制器需要用这个 TreeView 包装并动态添加 TreeItem。这就是问题所在,它没有加载那些 TreeItem。这是下面的测试代码,来自我的控制器:

public class MyControllerClass extends Application {

    @FXML
    private TreeView<String> locationTreeView;

    @Override
    public void start(Stage stage) throws Exception {

        stage.initStyle(StageStyle.TRANSPARENT);
        stage.getIcons().add(new Image(getClass().getResourceAsStream("myIcon.png")));

        Parent root = FXMLLoader.load(getClass().getResource("myInterface.fxml"));

        Scene scene = new Scene(root);
        stage.setScene(scene);

        loadTreeItems();

        stage.show();
    }

    // Just a simple example that still doesn't works
    private void loadTreeItems() {

        try {
            TreeItem<String> root = new TreeItem<String>("Root Node");
            root.setExpanded(true);
            root.getChildren().addAll(
                new TreeItem<String>("Item 1"),
                new TreeItem<String>("Item 2"),
                new TreeItem<String>("Item 3")
            );

            locationTreeView = new TreeView<String>(root);

        } catch (Exception exc) {
            System.out.println("Error: " + exc.getMessage());
        }
    }

    public static void main(String[] args) {
         launch(args);
    }
}

Any ideas why its not working?

任何想法为什么它不起作用?

回答by jewelsea

There are a couple of reasons why your application doesn't work:

您的应用程序不起作用的原因有两个:

  1. You need to make the Controller and the Application separate classes.
  2. You should allow the FXML system to inject the TreeView instance rather than creating a new instance (as Aaron points out in his answer).
  1. 您需要使控制器和应用程序分开的类。
  2. 您应该允许 FXML 系统注入 TreeView 实例而不是创建一个新实例(正如 Aaron 在他的回答中指出的那样)。

The way you have your application currently structured what will happen is:

您当前构建应用程序的方式是:

  1. The Java system will create an instance of MyControllerClasson startup (and invoke it's startmethod).
  2. The FXMLLoaderwill create another instanceof MyControllerClasseach timethe myInterface.fxmlfile is loaded.
  3. The FXMLLoaderwill create a new TreeViewinstance and perform the FXML injection on the locationTreeViewmember of the newMyControllerClassinstance it creates.
  4. The FXMLLoaderwill try to invoke the initializemethod on the new MyControllerClass(of which you have none).
  5. The FXMLLoaderwill notinvoke the startmethod on the newMyControllerClass.
  6. Your original startmethod invocation on your original MyControllerClasswill continue processing and create another newTreeViewinstance which it will set the locationTreeViewmember of the oldMyControllerClassinstance to.
  1. Java 系统将MyControllerClass在启动时创建一个实例(并调用它的start方法)。
  2. FXMLLoader将创造另一个实例MyControllerClass每一次myInterface.fxml文件被加载。
  3. FXMLLoader会创建一个新的TreeView实例和上执行FXML注射locationTreeView的成员MyControllerClass它创建实例。
  4. FXMLLoader会尝试调用initialize的新方法MyControllerClass(其中,你有没有)。
  5. FXMLLoader调用start的方法MyControllerClass
  6. 您对原始start方法的原始方法调用MyControllerClass将继续处理并创建另一个TreeView实例,它将实例的locationTreeView成员设置为。MyControllerClass


I updated your code to make the modifications I suggested above and the code now works. The updated code is available.

我更新了您的代码以进行我上面建议的修改,代码现在可以工作了。更新的代码可用

A sample screenshot from the running code is: dynamictreeview

运行代码的示例屏幕截图是: 动态树视图

MyApplicationClass.java

我的应用程序类.java

import javafx.animation.*;
import javafx.application.Application;
import javafx.event.*;
import javafx.fxml.FXMLLoader;
import javafx.scene.*;
import javafx.scene.image.Image;
import javafx.scene.input.MouseEvent;
import javafx.stage.*;
import javafx.util.Duration;

/** Sample application to demonstrate programming an FXML interface. */ 
public class MyApplicationClass extends Application {
  @Override public void start(final Stage stage) throws Exception {
    // load the scene fxml UI.
    // grabs the UI scenegraph view from the loader.
    // grabs the UI controller for the view from the loader.
    final FXMLLoader loader = new FXMLLoader(getClass().getResource("myInterface.fxml"));
    final Parent root = (Parent) loader.load();
    final MyControllerClass controller = loader.<MyControllerClass>getController();

    // continuously refresh the TreeItems.
    // demonstrates using controller methods to manipulate the controlled UI.
    final Timeline timeline = new Timeline(
      new KeyFrame(
        Duration.seconds(3), 
        new TreeLoadingEventHandler(controller)
      )
    );
    timeline.setCycleCount(Timeline.INDEFINITE);
    timeline.play();

    // close the app if the user clicks on anywhere on the window.
    // just provides a simple way to kill the demo app.
    root.addEventFilter(MouseEvent.MOUSE_CLICKED, new EventHandler<MouseEvent>() {
      @Override public void handle(MouseEvent t) {
        stage.hide();
      }
    });

    // initialize the stage.
    stage.setScene(new Scene(root));
    stage.initStyle(StageStyle.TRANSPARENT);
    stage.getIcons().add(new Image(getClass().getResourceAsStream("myIcon.png")));
    stage.show();
  }

  /** small helper class for handling tree loading events. */
  private class TreeLoadingEventHandler implements EventHandler<ActionEvent> {
    private MyControllerClass controller;
    private int idx = 0;

    TreeLoadingEventHandler(MyControllerClass controller) {
      this.controller = controller;
    }

    @Override public void handle(ActionEvent t) {
      controller.loadTreeItems("Loaded " + idx, "Loaded " + (idx + 1), "Loaded " + (idx + 2));
      idx += 3;
    }
  }

  // main method is only for legacy support - java 8 won't call it for a javafx application.
  public static void main(String[] args) { launch(args); }
}

MyControllerClass.java

我的控制器类.java

import javafx.fxml.FXML;
import javafx.scene.control.*;

/** Sample controller class. */ 
public class MyControllerClass {
  // the FXML annotation tells the loader to inject this variable before invoking initialize.
  @FXML private TreeView<String> locationTreeView;

  // the initialize method is automatically invoked by the FXMLLoader - it's magic
  public void initialize() {
    loadTreeItems("initial 1", "initial 2", "initial 3");
  }  

  // loads some strings into the tree in the application UI.
  public void loadTreeItems(String... rootItems) {
    TreeItem<String> root = new TreeItem<String>("Root Node");
    root.setExpanded(true);
    for (String itemString: rootItems) {
      root.getChildren().add(new TreeItem<String>(itemString));
    }

    locationTreeView.setRoot(root);
  }
}

myInterface.fxml

我的接口文件

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns:fx="http://javafx.com/fxml" fx:controller="test.MyControllerClass">
  <TreeView fx:id="locationTreeView" layoutX="0" layoutY="0" prefHeight="193.0" prefWidth="471.0" />
</AnchorPane>

回答by Aaron

In your loadTreeItems() function you are creating a new TreeView instance. This is replacing the one that is defined in your FXML file and attached to your scene with a new instance that is not part of the scene graph.

在您的 loadTreeItems() 函数中,您正在创建一个新的 TreeView 实例。这是用不属于场景图的新实例替换在 FXML 文件中定义并附加到场景的实例。

To add items to a TreeView that was created via the FXMLLoader you can use the setRoot()function.

要将项目添加到通过 FXMLLoader 创建的 TreeView,您可以使用setRoot()函数。

private void loadTreeItems() {

    try {
        TreeItem<String> root = new TreeItem<String>("Root Node");
        root.setExpanded(true);
        root.getChildren().addAll(
            new TreeItem<String>("Item 1"),
            new TreeItem<String>("Item 2"),
            new TreeItem<String>("Item 3")
        );

        locationTreeView.setRoot(root);

    } catch (Exception exc) {
        System.out.println("Error: " + exc.getMessage());
    }
}