问题
I have started using JavaFX to create a window for user interaction, to be used in another, non-JavaFX, program.
My main program is called the Abc
class, having a main method. This is a non-JavaFX program, but plain vanilla Java. This program runs some activities, then asks the user to select a String
from a list of possible Strings
. This user interaction is done with a JavaFX program called MenuSelector
. The user selects one String
, which will then be returned to Abc
for further processing.
MenuSelector
is made with Netbeans and Scene Builder, which uses three files: FXMLDocument.fxml
, FXMLDocumentController.java
, and MenuSelector.java
. Handling of the selection process is done in FXMLDocumentController.java
. The MenuSelector.java
class only defines the Stage
and Scene
.
What I can not find online is instructions for how Abc
would start MenuSelector
. And can not find instructions how the resulting String
in FXMLDocumentController
can be passed back to Abc
. What steps should I take to get this up and running?
Edit:
I was asked about the reason for having things this way. It was suggested that I am having a design problem.
My current implementation of the MenuSelector
is with using javax.swing
. This is hand-coded in one java class called MenuSelector
, having a top level method which can be called from other programs (e.g. Abc
, Def
, ..). This MenuSelector
is a supporting piece of software which can be used by multiple programs. Each program can submit a list of Strings to the menu, out of which the user can select one String
, which will then be returned to the program which called this MenuSelector
.
The MenuSelector
therefore does not have a main method, only a top-level method which can be called by others. MenuSelector
is a supporting program, and is not supposed to be the highest level.
My attempt is to replace the hand-coded javax.swing
version of MenuSelector
by a JavaFX version. But I can only do this if I can input a list of Strings as input to the menu, and return a single String as result.
Edit, to explain the JavaFX structure as generated by Netbeans:
When generating a new Netbeans JavaFX project it comes with three files: FXMLDocument.fxml
, FXMLDocumentController.java
, and MenuSelectorFX.java
. After building the GUI with Scene Builder and creating the control software the contents of these three are:
FXMLDocument.fxml
(not manually modified by me):
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ChoiceBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.VBox?>
<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.171" fx:controller="menuselector.FXMLDocumentController">
<children>
<VBox alignment="CENTER" prefHeight="200.0" prefWidth="320.0" spacing="20.0">
<children>
<ChoiceBox fx:id="InstrumentChoiceBox" prefWidth="150.0" />
<Button mnemonicParsing="false" onAction="#onChoiceMade" text="This One" />
<Label fx:id="SelectionLabel" text="Label" />
</children>
</VBox>
</children>
</AnchorPane>
FXMLDocumentController.java
(code added by me):
package menuselector;
import java.net.URL;
import java.util.ArrayList;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Label;
public class FXMLDocumentController implements Initializable {
@FXML private ChoiceBox InstrumentChoiceBox;
@FXML private Label SelectionLabel;
private String SelectedInstrument;
public String getSelectedInstrument(){
return SelectedInstrument;
}
public void onChoiceMade(){
SelectedInstrument = InstrumentChoiceBox.getSelectionModel().getSelectedItem().toString();
SelectionLabel.setText("Instrument selected: "+SelectedInstrument);
}
private String[] determineCandidates(){
//method to determine the list of candidates, abbreviated
//Reads in a number of Strings from file and converts to String[]
return Result;
}
@Override
public void initialize(URL url, ResourceBundle rb) {
String[] Candidates = determineCandidates();
SelectedInstrument = "";
for(int i = 0; i < Candidates.length;i++) InstrumentChoiceBox.getItems().add(Candidates[i]);
InstrumentChoiceBox.setValue(Candidates[0]);
}
}
and finally MenuSelectorFX
(not manually modified by me):
package menuselector;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class MenuSelectorFX 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);
}
}
Until now have I been able to call the MenuSelector from my non-JavaFX class Abc
(or Def
) by using the lines:
MenuSelectorFX msfx = new MenuSelectorFX();
msfx.main(new String[0]);
This starts the GUI with the choice box, and when I select an option the chosen option is displayed in the GUI. What I have not yet been able to achieve is the return of parameter SelectedInstrument
in FXMLDocumentController
to Abc
.
回答1:
I made a small program to build what I understood you are trying to do. The Abc
class starts and get some input from the user. With that input we start the JavaFX Window with some options in a combobox. The picked value from the combobox is returned to Abc
and we can use it.
The Abc
class:
package application;
import java.util.Scanner;
public class Abc {
public static void main(String[] args) {
System.out.println("Some activitives...");
System.out.println("Press 1 to start JAVAFX");
try(Scanner input = new Scanner(System.in)){
int one = input.nextInt();
if(one == 1) {
String[] inputedValue = new String[] {String.valueOf(one)};
MainJavaFX.main(inputedValue);
System.out.println("The user picked " + FXMLDocumentController.selectedValue);
System.out.println("Go on and use it...");
}
}
catch(Exception e) {
e.printStackTrace();
}
}
}
The MainJavaFX
application. Note I made a MyStrings
class to populate the combobox:
package application;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class MainJavaFX extends Application {
@Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Document.fxml"));
AnchorPane root = loader.load();
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
launch(args);
}
}
The Controller:
package application;
import java.net.URL;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.ChoiceBox;
import javafx.scene.control.Label;
public class FXMLDocumentController implements Initializable {
@FXML private ChoiceBox InstrumentChoiceBox;
@FXML private Label SelectionLabel;
@FXML private Button selectionButton;
static String SelectedInstrument;
public String getSelectedInstrument(){
return SelectedInstrument;
}
public void onChoiceMade(){
SelectedInstrument = InstrumentChoiceBox.getSelectionModel().getSelectedItem().toString();
SelectionLabel.setText("Instrument selected: "+SelectedInstrument);
selectionButton.getScene().getWindow().hide();
}
private String[] determineCandidates(){
//method to determine the list of candidates, abbreviated
//Reads in a number of Strings from file and converts to String[]
return new String[] {"a","b","c"};
}
@Override
public void initialize(URL url, ResourceBundle rb) {
String[] Candidates = determineCandidates();
SelectedInstrument = "";
for(int i = 0; i < Candidates.length;i++) InstrumentChoiceBox.getItems().add(Candidates[i]);
InstrumentChoiceBox.setValue(Candidates[0]);
}
}
The Document.fxml
:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ChoiceBox?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.VBox?>
<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.171" fx:controller="application.FXMLDocumentController">
<children>
<VBox alignment="CENTER" prefHeight="200.0" prefWidth="320.0" spacing="20.0">
<children>
<ChoiceBox fx:id="InstrumentChoiceBox" prefWidth="150.0" />
<Button fx:id="selectionButton" mnemonicParsing="false" onAction="#onChoiceMade" text="This One" />
<Label fx:id="SelectionLabel" text="Label" />
</children>
</VBox>
</children>
</AnchorPane>
And the output when running it:
Some activitives...
Press 1 to start JAVAFX
-- The JavaFX Window pops up --
The user picked b
Go on and use it...
EDIT: I added a controller class so you have the same structure. Note that the button has now an Id so I can use it to close the JavaFX after the user picked a value from the combobox. I also made the selected String value static so I can just call it from the Abc
class in my print.
回答2:
Usually you'd launch the Application
, use it as entry point of the app and start any other logic from it's start
/init
methods or as reaction to an event.
In your case though starting with JavaFX 9 you can use Platform.startup
to initialize the GUI. (Works only once though; Subsequent JavaFX logic would require you to use Platform.runLater
instead).
public static void main(String[] args) {
System.out.println("in main");
CompletableFuture<String> result = new CompletableFuture<>();
Platform.startup(() -> {
System.out.println("creating gui");
ListView<String> list = new ListView<>();
for (int i = 0; i <= 100; i++) {
list.getItems().add(Integer.toString(i));
}
Button submit = new Button("OK");
submit.disableProperty().bind(list.getSelectionModel().selectedItemProperty().isNull());
submit.setOnAction(evt -> {
String selection = list.getSelectionModel().getSelectedItem();
result.complete(selection);
});
Scene scene = new Scene(new VBox(list, submit));
Stage stage = new Stage();
stage.setScene(scene);
stage.setOnCloseRequest(evt -> {
result.completeExceptionally(new IOException("User failed to select an element"));
});
stage.show();
});
System.out.println("start waiting for result");
try {
System.out.println("result: " + result.get());
} catch (ExecutionException ex) {
// handle user failing to select an element
System.out.println(ex.getCause().getMessage());
} catch (InterruptedException ex) {
// TODO: make sure not to execute more logic except for shutting down after this
}
Platform.exit(); // shutdown javafx
}
For JavaFX < 9 you'd need to use Application.launch
to get the application up and runing though and use a static
CompletableFuture
to get your hands on an object allowing communication with the GUI (or the result, if you only need the GUI once). The logic to use would be similar to the one in the above snippet.
来源:https://stackoverflow.com/questions/59541555/how-to-achieve-javafx-and-non-javafx-interaction