在 JavaFX 中更改来自不同 class 的标签文本

Changing the text of a label from a different class in JavaFX

这个问题已经有人问过 here 但找不到任何答案。我重现了类似的情况,我想使用控制器

从另一个class更改标签的文本

FXMLDocumentController.java

public class FXMLDocumentController implements Initializable {

    @FXML
    private Label label;

    @FXML
    private void handleButtonAction(ActionEvent event) {
        System.out.println("FXMLDocumentController.#handleButtonAction");
        label.setText("Hello World!");
        Connector.Connecting();
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // TODO
    }    

    public void setLabelText(String text)
    {
        System.out.println("FXMLDocumentController.setLabelText(): Called");
        label.setText(text);
    }

}

FXMLDocument.fxml

<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml/1" fx:controller="demo5.FXMLDocumentController">
    <children>
        <Button layoutX="126" layoutY="90" text="Click Me!" onAction="#handleButtonAction" fx:id="button" />
        <Label layoutX="126" layoutY="120" minHeight="16" minWidth="69" fx:id="label" />
    </children>
</AnchorPane>

Demo5.java

public class Demo5 extends Application {

    @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();
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }

}

Connector.java

public class Connector {
    public static void Connecting() {
        try {
            System.out.println("Connector.Connecting(): Called");

            FXMLLoader loader = new FXMLLoader(FXMLDocumentController.class.getResource("FXMLDocument.fxml"));
            loader.load();
            FXMLDocumentController controller = (FXMLDocumentController) loader.getController();

            controller.setLabelText("Bye World");
        } catch (IOException ex) {
            Logger.getLogger(Connector.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

控制台输出

Connector.Connecting(): Called
FXMLDocumentController.setLabelText(): Called

但是看不到标签有什么变化。我是不是漏掉了什么重要的东西?

您的 Demo5 class 和连接器 class 都通过调用 FXMLLoader.load() 创建了 FXMLDocumentController 的唯一实例。 Demo5 class 中的实例被放置在场景图中并变得可见。连接器中的实例不可见。当您调用 setLabelText() 时,它正在更改一个看不见的标签的文本。您可能想要做的是在 Demo5 中获取 FXMLDocumentController 实例,并通过构造函数或 setter 方法将其提供给连接器 class。您可能需要根据连接器 class 的用途进行一些更改。或者,您可以使用连接器 class 加载 FXML 根和控制器并提供访问它们的方法,然后在 Demo5 中使用这些方法使场景可见。

您可以更改连接器 class 以接收控制器实例:

public class Connector {
    public static void Connecting(FXMLDocumentController controller) {
        try {
            System.out.println("Connector.Connecting(): Called");
            controller.setLabelText("Bye World");
        } catch (IOException ex) {
            Logger.getLogger(Connector.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}


public class FXMLDocumentController implements Initializable {

    @FXML
    private Label label;

    @FXML
    private void handleButtonAction(ActionEvent event) {
        System.out.println("FXMLDocumentController.#handleButtonAction");
        label.setText("Hello World!");
        Connector.Connecting(this);
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // TODO
    }    

    public void setLabelText(String text)
    {
        System.out.println("FXMLDocumentController.setLabelText(): Called");
        label.setText(text);
    }

}

注:

如果您的连接器将花费更长的时间来执行它需要执行的任何操作,您可能需要使用任务,这样您就不会冻结您的 UI。要更新标签,您必须绑定文本 属性,然后使用 updateMessage() 方法更新文本值。

public class FXMLDocumentController implements Initializable {

    @FXML
    private Label label;

    @FXML
    private void handleButtonAction(ActionEvent event) {
        System.out.println("FXMLDocumentController.#handleButtonAction");
        label.setText("Hello World!");

        Task<Boolean> connectorTask = new ConnectorTask();
        label.textProperty().bind(connectorTask.messageProperty());
        connectorTask.setOnSucceeded(e -> {
            // this is going to be called if the task ends up without error
            label.textProperty().unbind();
        });
        new Thread(connectorTask).start();
    }

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        // TODO
    }    

    //public void setLabelText(String text)
    //{
    //    System.out.println("FXMLDocumentController.setLabelText(): Called");
    //    label.setText(text);
    //}


    public class ConnectorTask extends Task<Boolean> {

        @Override
        protected Boolean call() throws Exception {
            // ... do whatever you need here

            // then you call this method to update the TextProperty from the Label that was bound.
            updateMessage("Bye World");

            return Boolean.TRUE;
        }
    }

}

我通过在 FXMLDocumentController.java:

中将 Label 定义为静态来以一种简单的方式实现它
@FXML GridPane myGridPane;
public static Label totLabel = new Label("Total"); 

并在 FXMLDocumentController 的初始化方法中将其添加到 myGridPane class:

@Override
public void initialize(URL url, ResourceBundle rb) {
    myGridPane.add(totLabel, 0, 3);
}

在任何其他 class 中,您可以像这样调用此标签的 setText():

String message = "this message will appear in the total label";//your string
FXMLDocumentController.totLabel.setText(message);