[JAVAFX]TextFiledに入力された値の取得方法
checkベストアンサー
+2
ユーザーとの対話をするための小さな画面を一般にダイアログと呼びます。多分画面Aはダイアログとして設計できると思います。
ダイアログを実装するためには通常の画面(JavaFXではStage)と同様にもできます。質問者さんはおそらくそういうイメージをお持ちであろうと思います。しかしながら大抵のGUIライブラリーと同様JavaFXもダイアログを簡便に実装するための特化機能が用意されておりそちらを使う方がより簡単と思います。
JavaFXでのダイアログはjavafx.scene.control.Dialogというベースクラスおよびその派生として定義することが想定されていますが、本件のように文字列テキストの入力なら専用クラスjavafx.scene.control.TextInputDailogが用意されていますのでそれを使うのがよいと思います。
簡単な例を挙げてみますと以下のようになります。
TextInputDialog dialog = new TextInputDialog("") {
{
setTitle("filtering");
setHeaderText("input filtering text");
}
};
Optional<String> optInput = dialog.showAndWait();
if (!optInput.isPresent()) {
// キャンセルが押された場合optInputは値を持たない
System.out.println("cancelled");
} else {
// OKが押された場合はoptInputは値を持ち、それはgetにより得られる。
System.out.println("input text = " + optInput.get());
}
上記ではデフォルトから2点のみ動作を変えています。一つはダイアログタイトルで、もう一つはヘッダーテキストです。様々なカスタマイズをするためにはjavafx.control.Dialogの機能を調べるとやり方がわかってきますが、とりあえずはデフォルトの振る舞いがどのようになっているかを観察してみるとよいでしょう。
他のコントローラクラスにその入力された値を渡す方法が分からず悩んでいます。
ダイアログを用いる場合、ユーザーの入力結果はshowAndWaitメソッドの戻り値になるので、それを用いることになります。本件の場合、画面Bのコントローラーインスタンスへなんらかの方法で渡すのが自然と思います。渡し方は画面Bをどうやって生成・表示するかによって若干のバリエーションがありますが、FXMLとFXMLLoaderにより画面を生成する方法を用いるなら、以下のようにFXMLロード後にコントローラーインスタンスを取得しそこに定義したメソッドを通じて設定という流れが典型的な方法の一つだと思います。
FXMLLoader loader = new FXMLLoader(...);
loader.load();
ControllerB controllerB = loader.getController();
controllerB.setFilterText(optInput.get());
追記:
回答コメントから質問者さんの想定がダイアログではないようですので、各画面のStage, コントローラーを画面遷移の都度生成する前提に注意しつつ、画面遷移方法を考えてみました。以下の例は唯一の方法でもベストなものというわけではありません。やりかたには微妙なバリエーションがあるでしょう。つまり「ある画面から別の画面へ情報を引き渡す」実装例の一つです。
画面遷移メソッド(アプリケーションクラスに用意する例)
public class MyApp extends Application {
private static MyApp app;
public static MyApp getInstance() {
return Objects.requireNonNull(app);
}
public MyApp() { app = this; }
HashMap<Class<? extends Initializable>, String> fxmlMap = new HashMap<>();
@Override
public void start(Stage stage) {
fxmlMap.put(AController.class, "A.fxml");
fxmlMap.put(BController.class, "B.fxml");
try {
Parent root = FXMLLoader.load(getClass().getResource("Main.fxml"));
stage.setScene(new Scene(root));
stage.show();
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
public <C extends Initializable> C showStage(Class<C> controllerClass) throws IOException {
FXMLLoader loader = new FXMLLoader(getClass().getResource(fxmlMap.get(controllerClass)));
Parent parent = loader.load();
Stage stage = new Stage();
stage.setScene(new Scene(parent));
stage.show();
return loader.getController();
}
}
画面Aから画面Bへの遷移処理(検索ボタンのアクション)
public class AController implements Initializable {
@FXML
private TextField filterText;
@Override
public void initialize(URL location, ResourceBundle resources) {
}
@FXML
private void onShowB(ActionEvent ev) throws IOException {
Stage stage = (Stage)((Node)ev.getSource()).getScene().getWindow();
stage.hide();
BController bController = MyApp.getInstance().showStage(BController.class);
bController.setFilterText(filterText.getText());
}
}
画面Bにて検索条件を受け取るメソッド
public class BController implements Initializable {
@FXML
TableView<Model> tableView;
@Override
public void initialize(URL location, ResourceBundle resources) {
...
}
public void setFilterText(String text) {
// 指定テキストによりモデルを生成する(DBアクセスしてモデルインスタンスのリストを生成)
// 時間がかかる処理ならバックグラウンドスレッドで行うべきだが、ここでは簡単のため
// アプリケーションスレッドでやると想定
List<Model> modelList = ...;
tableView.getItems().setAll(modelList);
}
}
自分が考えた点は以下です。
- 多数の画面があるなら遷移処理を共通化したい
アプリケーションクラスでshowStageというメソッドを用意し各画面ではこれを使う想定としました。また本質問の目的にあるように画面間での情報の引き渡しを遷移元と遷移先のコントローラーインスタンス間で行えるよう、画面遷移メソッドは遷移先のコントローラーを返すようにしています。 - ステージのshowとコントローラークラスへの情報のやりとり順序
例を簡単にするため共通メソッド(showStage)は無条件に生成したstageをshowし、その後にstageに結び付いたコントローラーインスタンスに対して情報設定メソッドを呼び出すと想定しています。viewがいかなる状態であろうと(表示前であろうと表示後であろうと)コントローラーやモデルの状態を変更した際にビューが適切に追従するように作ることは難しくないのですが、モデルの生成に時間がかかるような場合、画面を表示後にしばらくして表示内容が変わるのか、表示内容を確定させてから画面を表示させるかで見栄えが変わると思います。そうした点は細かなチューニング対象だと思います。 - staticの仕様は最小限にする(追記2)
本例ではアプリケーションインスタンスをシングルトンとしてMyApp#app及びMyApp#getInstanceのみstaticにしています。こうしておくと(殆どの定義がインスタンスフィールド、インスタンスメソッドのため)アプリケーションインスタンスを複数生成するような設計に変更する場合でも変更の影響を最小限にできると思います。Javaに限らずstatic(=グローバル)はできるだけ避けるという考え方があると思いますのでそれに倣ったつもりです。そういう想定が必要ないならこうした点をいちいち気にせず適宜staticにしてもよいとは思います。
shibayun
2018/02/06 10:45
ダイアログを用いるという考え方は無かったので、とても参考になりました。一度試してみます。
ですが、ダイアログだと前画面に戻るためのボタンなどの配置はできないのでしょうか?
質問では二画面で説明しましたが、実際には10画面以上あるので...。
現在制作してある画面Aを追記しました。
shibayun
2018/02/06 10:56
KSwordOfHaste
2018/02/06 11:54
KSwordOfHaste
2018/02/06 12:03
shibayun
2018/02/06 12:24
例えば「戻る」ボタンを押下した場合は1つ前の画面(FXMLファイル)をFXMLLoader.loadで呼び出しています。
shibayun
2018/02/06 12:29
そうですね、複数の画面を表示するような動作はありません。
Stageについては、私が知識不足なばかりに現状は各画面でStage生成をしてしまっています。共有はしていません。
KSwordOfHaste
2018/02/06 14:44
KSwordOfHaste
2018/02/06 18:22
shibayun
2018/02/07 16:46
申し訳ないのですが、BControllerの 'List<Model> modelList = ...;' とはいったい何なのでしょうか?度々すみません。
KSwordOfHaste
2018/02/07 17:05
本件ですと引数に渡された検索条件に従ってDBアクセスし、結果セットの各行をModelクラスのインスタンスへと変換しそれをListとして蓄積するイメージです。TableViewは「なんらかのモデルクラスのインスタンス」を各行として表示しますよね?DBアクセスした結果の行をモデルクラスのインスタンスと見做すのはそういう意味合いです。