无法访问其他控制器的字段

时间:2017-06-10 22:04:25

标签: java user-interface javafx

我正在尝试在JavaFX中编写GUI应用程序。基本的想法是有一个主要的GridPane视图,在其中 - 另一个应该在按下按钮上更改的窗格。由于这个窗格必须从用户那里读取一些输入,我希望控制器之间有流,以便我可以在应用程序的不同部分之间传递数据。我的想法是将所有控制器放在一个地方,并从任何地方引用他们的字段。它似乎没有工作 - 似乎我不能从它外部修改窗格的内容,即使我可以访问它的字段。看起来或多或少是这样的:

main_view.fxml

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

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.Pane?>
<?import javafx.scene.layout.RowConstraints?>
<?import javafx.scene.layout.VBox?>

<GridPane alignment="center" hgap="10" vgap="10" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="main.MainController">
   <columnConstraints>
      <ColumnConstraints />
   </columnConstraints>
   <rowConstraints>
      <RowConstraints />
   </rowConstraints>
   <children>
      <GridPane prefHeight="300.0" prefWidth="400.0">
        <columnConstraints>
          <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="30.0" prefWidth="100.0" />
          <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
        </columnConstraints>
        <rowConstraints>
          <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
          <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
        </rowConstraints>
         <children>
            <VBox alignment="CENTER" prefHeight="200.0" prefWidth="100.0" spacing="20.0" GridPane.rowSpan="2">
               <children>
                  <TextField fx:id="userInputTextField">
                     <VBox.margin>
                        <Insets left="5.0" right="5.0" />
                     </VBox.margin>
                  </TextField>
                  <Button mnemonicParsing="false" onAction="#appendToFirstArea" text="Append to area 1" />
                  <Button mnemonicParsing="false" onAction="#appendToSecondArea" text="Append to area 2" />
                  <Button mnemonicParsing="false" onAction="#moveToFirstArea" text="Move to area 1" />
                  <Button mnemonicParsing="false" onAction="#moveToSecondArea" text="Move to area 2" />
               </children>
               <GridPane.margin>
                  <Insets />
               </GridPane.margin>
            </VBox>
            <Pane fx:id="content" prefHeight="300.0" prefWidth="280.0" GridPane.columnIndex="1" GridPane.rowSpan="2" />
         </children>
      </GridPane>
   </children>
</GridPane>

MainController.java

package main;

import javafx.event.ActionEvent;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Pane;

import java.io.IOException;

public class MainController {
    public TextField userInputTextField;
    public Pane content;

    private FirstAreaController getFirstAreaController() {
        return Main.firstAreaController;
    }

    private SecondAreaController getSecondAreaController() {
        return Main.secondAreaController;
    }

    public void appendToFirstArea(ActionEvent actionEvent) {
        String text = userInputTextField.getText();
        getFirstAreaController().firstTextArea.appendText(text + "\n");
    }

    public void appendToSecondArea(ActionEvent actionEvent) {
        String text = userInputTextField.getText();
        getSecondAreaController().secondTextArea.appendText(text + "\n");
    }

    public void moveToFirstArea(ActionEvent actionEvent) {
        try {
            GridPane firstArea = FXMLLoader.load(getClass().getResource("first_area.fxml"));
            content.getChildren().clear();
            content.getChildren().add(firstArea);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void moveToSecondArea(ActionEvent actionEvent) {
        try {
            GridPane secondArea = FXMLLoader.load(getClass().getResource("second_area.fxml"));
            content.getChildren().clear();
            content.getChildren().add(secondArea);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

first_area.fxml

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

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>

<GridPane onKeyPressed="#appendTest" maxHeight="-Infinity" maxWidth="-Infinity"
          minHeight="-Infinity" minWidth="-Infinity"
          prefHeight="300.0" prefWidth="280.0"
          xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.111"
          fx:controller="main.FirstAreaController">
    <columnConstraints>
        <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/>
    </columnConstraints>
    <rowConstraints>
        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES"/>
    </rowConstraints>
    <children>
        <TextArea fx:id="firstTextArea" prefHeight="200.0" prefWidth="200.0">
            <GridPane.margin>
                <Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/>
            </GridPane.margin>
        </TextArea>
    </children>
</GridPane>

FirstAreaController.java

package main;

import javafx.scene.control.TextArea;

public class FirstAreaController {
    public TextArea firstTextArea;
}

second_area.fxml

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

<?import javafx.geometry.Insets?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.layout.ColumnConstraints?>
<?import javafx.scene.layout.GridPane?>
<?import javafx.scene.layout.RowConstraints?>

<GridPane maxHeight="-Infinity" maxWidth="-Infinity"
          minHeight="-Infinity" minWidth="-Infinity"
          prefHeight="300.0" prefWidth="280.0"
          xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.111"
          fx:controller="main.SecondAreaController">
    <columnConstraints>
        <ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0"/>
    </columnConstraints>
    <rowConstraints>
        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES"/>
    </rowConstraints>
    <children>
        <TextArea fx:id="secondTextArea" prefHeight="200.0" prefWidth="200.0">
            <GridPane.margin>
                <Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/>
            </GridPane.margin>
        </TextArea>
    </children>
</GridPane>

SecondAreaController.java

package main;

import javafx.scene.control.TextArea;

public class SecondAreaController {
    public TextArea secondTextArea;
}

Main.java

package main;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application {

    public static MainController mainController;
    public static FirstAreaController firstAreaController;
    public static SecondAreaController secondAreaController;

    @Override
    public void start(Stage primaryStage) throws Exception {
        FXMLLoader loader = new FXMLLoader();
        loader.setLocation(getClass().getResource("main_view.fxml"));
        Parent root = loader.load();
        primaryStage.setTitle("Test");
        primaryStage.setScene(new Scene(root, 400, 300));
        mainController = loader.getController();

        FXMLLoader firstLoader = new FXMLLoader();
        firstLoader.setLocation(getClass().getResource("first_area.fxml"));
        firstLoader.load();
        firstAreaController = firstLoader.getController();

        FXMLLoader secondLoader = new FXMLLoader();
        secondLoader.setLocation(getClass().getResource("second_area.fxml"));
        secondLoader.load();
        secondAreaController = secondLoader.getController();

        primaryStage.setResizable(false);
        primaryStage.show();
    }

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

按下“附加到1区域”按钮后,没有任何反应 - 文本未显示在TextArea中。虽然当我添加一个仅在FirstAreaController内部调用的方法时,文本会出现在预期的区域中。我该如何解决?

1 个答案:

答案 0 :(得分:1)

您正在加载when两次:一次加载Handler#postAtFrontOfQueue(),加载second_area.fxml并显示,然后加载一次。您正在访问的控制器是与您在MainController中加载second_area.fxml时加载的UI关联的控制器,但您实际显示的UI是您在second_area.fxml中加载的UI。因此,当您在控制器上调用方法时,您正在更新从未显示过的UI。

无论如何,公共领域的“全球数据”方法无论如何都不是一个特别好的主意,并且(从许多有充分记录的理由)从长远来看不太可能维持。

由于您只是从Main访问这些内容,因此只需在MainController类中创建MainControllerFirstAreaController字段,并在加载时设置其值(和显示)相应的FXML文件:

SecondAreaController

如果您需要从“更远的”对象更新UI,请考虑使用数据模型和MVC方法。见,例如, Applying MVC With JavaFx