web-dev-qa-db-ja.com

JavaFXの別のコントローラークラスからUI要素にアクセスするにはどうすればよいですか?

私はJavaFX/Java 8アプリケーションをNetBeans 8で作成しました(SceneBuilderなし)。

私のアプリケーションには、独自のFXMLファイル(primary.fxml)と独自のコントローラークラス(FXMLPrimaryController.Java)を持つメインウィンドウがあります。 FXMLの項目の1つはTextAreaです。 FXMLPrimaryController.Javaのメソッドのいくつかは、そのTextAreaに追加するものです。

このアプリケーションは、独自のFXML(second.fxml)と独自のコントローラークラス(FXMLsecondController.Java)を持つ2番目のウィンドウ(別の「ステージ」)を生成します。

2番目のコントローラークラス内で、プライマリのTextAreaにアクセスするにはどうすればよいですか?

関連するコードのサンプルを次に示します。

primary.fxml:

<Button text="press me!" onAction="#openSecondWindow" />
<TextArea fx:id="myArea" />

FXMLPrimaryController.Java:

public class FXMLPrimaryController implements Initializable {

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

    @FXML private TextArea myArea;

    final public void writeToTextArea() {
        myArea.appendText("hi!");
    }

    @FXML
    private void openSecondWindow(ActionEvent event) throws Exception {

        Group root = new Group();
        Stage stage = new Stage();

        AnchorPane frame = FXMLLoader.load(getClass().getResource("second.fxml"));
        root.getChildren().add(frame);
        Scene scene = new Scene(root);

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

}

Second.fxmlに特別なことはありません。 onAction="#writeSomething"

FXMLsecondController.Javaでは、上記のTextAreaを参照する関数が必要です。

13
adeena

セカンダリコントローラのFXMLをロードするときは、次のようにします。

_FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("second.fxml"));
AnchorPane frame = fxmlLoader.load();
FXMLSecondaryController c = (FXMLSecondaryController) fxmlLoader.getController();
_

次に、2番目のコントローラーに参照を渡すことができます。これは単にTextAreaである可能性があります。

編集:

上記のスニペットのload()呼び出しを置き換え、setLocation()呼び出しを追加しました。古い行AnchorPane frame = fxmlLoader.load(getClass().getResource("second.fxml"));は、静的なload関数を呼び出していたため間違っていましたが、ここでは役に立ちません。

編集:

(上記のコードスニペットを変数名とより一致するように変更しました。)上記のコードスニペットは、コードのこの部分を置き換えます。

_AnchorPane frame = FXMLLoader.load(getClass().getResource("second.fxml"));
_

この行はFXMLLoaderを使用してビューをロードし、コントローラーのインスタンス(この場合はFXMLSecondaryController)も作成します。ただし、静的な_FXMLLoader.load_メソッドを使用することはできません。FXMLLoaderのインスタンスが必要です。そのインスタンスは、ロード後にコントローラーへの参照を保持し、getController()を使用してそれを取得できます。そして、戻り値をコントローラークラスにキャストする必要があります(_(FXMLSecondaryController)_を使用)。

プライマリコントローラにはフィールドがあります:

_@FXML private TextArea myArea;
_

これはTextAreaへの参照を保持し、fxmlローダーによって初期化されます。 (_@FXML_アノテーションを削除すると、ローダーはそれに触れません。)

セカンダリコントローラで、次のフィールドを追加します。

_public TextArea primaryTextArea;
_

_@FXML_がなくなっている(fxmlローダーはフィールドに触れない)ことに注意してください。また、private(フィールドを設定する必要があるため、他のユーザーも参照する必要があります)。これで、コントローラがロードされた後にこのフィールドを設定できます。読み込みコードに戻ります。

_FXMLLoader fxmlLoader = new FXMLLoader();
fxmlLoader.setLocation(getClass().getResource("second.fxml"));
AnchorPane frame = fxmlLoader.load();
FXMLSecondaryController c = (FXMLSecondaryController) fxmlLoader.getController();
// Add this too:
c.primaryTextArea = myArea;
_

編集:上記のスニペットのload()呼び出しを置き換え、setLocation()呼び出しを追加しました。古い行AnchorPane frame = fxmlLoader.load(getClass().getResource("second.fxml"));は、静的なload関数を呼び出していたため間違っていましたが、ここでは役に立ちません。

FXMLSecondaryControllerに、プライマリコントローラーのTextAreaへの参照が追加されました。そのメソッドの1つで、そのコンテンツにアクセスできるはずです。

_public class FXMLSecondaryController implements Initializable {
    // ...

    public TextArea primaryTextArea;

    // ...

    @FXML
    private void doSomething(ActionEvent event) throws Exception {
        primaryTextArea.appendText("Hi ho!");
    }

}
_

このソリューションは最善の方法ではありませんが、シンプルであり、最初に使用できることに注意してください。バインディングの使用は確かに推奨されます。データを保持し、他のコントローラーがバインドするクラスを作成しようとします。しかし、今のところ単純なアプローチをとれば、それで十分でしょう。

12
Rainer Schwarze

FXMLPrimaryControllerからTextFieldを公開し、FXMLsecondControllerにTextFieldプロパティを含めることができれば幸いです。次に、部品を組み立てるときにそのプロパティを設定します。ただし、コントローラーを介してビューの詳細を公開するため、これは良い設計とは言えません。

TextAreaは必要ないが、second.fxmlのTextAreaのデータは不要だと思います。したがって、各コントローラーでStringプロパティを公開し、それらをバインドすることをお勧めします。

public class FXMLPrimaryController {  
     private ReadOnlyStringWrapper text ;  

     @FXML 
     private TextArea myArea;

     public void initialize() {  
          text= new ReadOnlyStringWrapper(this, "text");  
          text.bind(myArea.textProperty());  
     }  
     public ReadOnlyStringProperty textProperty() {  
          return text.getReadOnlyProperty();  
     }  
     public String getText() {  
          return text.get();  
     }  

    @FXML
    private void openSecondWindow(ActionEvent event) throws Exception {

    Group root = new Group();
    Stage stage = new Stage();

    AnchorPane frame = FXMLLoader.load(getClass().getResource("second.fxml"));
    root.getChildren().add(frame);
    Scene scene = new Scene(root);

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

そして、FXMLsecondControllerで、

public class FXMLsecondController {  
     private StringProperty text ;  
     @FXML  
     private void handleButtonAction(ActionEvent event) {  
          System.out.println(text.getValue());  
     }  
     public void initialize() {  
          text = new SimpleStringProperty(this, "text");  
     }  
     public StringProperty textProperty() {  
          return text ;  
     }  
     public String getText() {  
          return text.get();  
     }  
}

次に、このようなものを使用して、これら2つをバインドできます

FXMLLoader primaryLoader = new FXMLLoader(getClass().getResource("primary.fxml"));  
Parent textAreaHolder = (Parent) primaryLoader.load();  
FXMLLoader secondaryLoader = new FXMLLoader(getClass().getResource("second.fxml"));  
Parent textAreaUser = (Parent) secondaryLoader.load();  
FXMLPrimaryController primaryController = (FXMLPrimaryController) textAreaHolder.getController();  
FXMLsecondController secondController = (FXMLsecondController) textAreaUser.getController();  
secondController.textProperty().bind(primaryController.textProperty());

これにより、両方のコントローラーの変数textがバインドされ、プライマリコントローラーのテキストエリアに入力された値をセカンダリコントローラーで簡単に使用できます。

3
ItachiUchiha