I've got single window JavaFX application, created from one of the JavaFX tutorial.
I'm setting new window content by following function:
private Initializable replaceSceneContent(final String fxml) throws Exception {
// wczytanie fxml
FXMLLoader loader = new FXMLLoader();
InputStream in = Main.class.getResourceAsStream(fxml);
loader.setBuilderFactory(new JavaFXBuilderFactory());
loader.setLocation(Main.class.getResource(fxml));
AnchorPane page;
try {
page = (AnchorPane) loader.load(in);
} finally {
in.close();
}
Scene scene = new Scene(page, w, h);
stage.setScene(scene);
return (Initializable) loader.getController();
}
But i want to choose one of TextFields from this fxml file to be active by default. How to do this? I've tried to call requestFocus method in controller's initialize method but it didn't work. I haven't found any suitable property in TextField class either in AnchorPane class (AnchorPane is root element of fxml controls tree).
Try wrapping your requestFocus() call with PlatForm.runlater
Platform.runLater(new Runnable() {
public void run() {
textField.requestFocus();
}
});
Related
I'm still new in Java, and I have a problem with passing objects between two windows in JavaFX.
I have mainStage with mainStageController. There are two things inside the controller: a label and a button. The button is opening a new window (childStage) with childStageController.
In childStageController I have a method, which gets the text "TEST CONTENT" from textField. I want to pass this text to the existing mainStage and set the label text to "TEST CONTENT".
There are a lot of topics handle similar problems, but in every case I found, the problem is with passing the object to a new created window. My case is exactly the opposite: I need to pass the object from the new opened window to the existing one.
#FXML
private Label label;
#FXML
private Button button;
private void configureButton() {
button.setOnAction(event -> {
newStageOpener(mainPaneResource, mainPaneStageTitle);
// There is magic to do
label.setText(message);
});
}
private void newStageOpener(String resource, String stageTitle){
try {
Parent parent = FXMLLoader.load(getClass().getResource(resource));
Scene scene = new Scene(parent);
Stage stage = new Stage();
stage.setScene(scene);
stage.setTitle(stageTitle);
stage.showAndWait();
} catch (IOException e) {
e.printStackTrace();
}
}
I try to do something like this, but every time I get a NullPointerException (the debugger says that childStageController is null).
FXMLLoader loader = new FXMLLoader(getClass().getResource(childPaneResource));
ChildStageController childStageController = loader.getController();
String message = childStageController.getMessage;
I also try: using setLocation, using loader.load(), "reverse" problem (setText directly from childStageController, instead of getting it from mainStageController), put newStageOpener into a configureButton, but nothing works - I always get the same NullPointerException.
What should I do to fix it?
There was mistake in newStageOpener - I rebuild it like this:
private void newStageOpener(String resource, String stageTitle) {
try {
FXMLLoader loader = new FXMLLoader(ChildStageController.class.getResource(resource));
Parent root = loader.load();
Scene scene = new Scene(root);
Stage stage = new Stage();
stage.setScene(scene);
stage.setTitle(stageTitle);
stage.showAndWait();
getObjectFromResource(resource, loader);
} catch (IOException e) {
e.printStackTrace();
}
}
private void getObjectFromResource(String resource, FXMLLoader loader) {
loader.setLocation(ChildStageController.class.getResource(resource));
ChildStageController childStageController = loader.getController();
message = childStageController.getMessage();
}
getMessage obviously is getting text from TextField.
Now it's working perfect.
I cant set the text of a Textfield. There is no error but the Textfield is still empty.
The rest of the program is working, the method is called and the System.out.println(...) prints the right text.
So the problem is, the text of the textfield cant be set. Even if i just write textField.setText("0"); the textfield is still empty.
When I set the text for the textfield under public void initialize(...) it also works. So why doesn't it work in setCurrentInfo?
#FXML
private TextField textField;
private Info currentInfo;
#Override
public void initialize(URL url, ResourceBundle rb) {
}
public void setCurrentInfo(Info currentInfo) {
textField.setText(currentInfo.getpw1());
System.out.println(currentInfo.getpw1());
this.currentInfo = currentInfo;
}
The part of the controller where setCurrentInfo is called:
#FXML
private void handleBtn1(ActionEvent event) throws Exception{
Info info = new Info(textFieldA.getText(), textFieldB.getText());
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("FXMLPassword.fxml"));
loader.load();
Stage stage;
Parent root;
if(event.getSource()==btn1){
//get reference to the button's stage
stage=(Stage) btn1.getScene().getWindow();
//load up OTHER FXML document
root = FXMLLoader.load(getClass().getResource("FXMLPassword.fxml"));
}
else{
stage=(Stage) btn1.getScene().getWindow();
root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
}
//create a new scene with root and set the stage
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
FXMLPasswordController passwordController = loader.getController();
passwordController.setCurrentInfo(info);
}
You are retrieving the controller from the wrong FXMLLoader. In particular, you do:
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("FXMLPassword.fxml"));
loader.load();
and then later
FXMLPasswordController passwordController = loader.getController();
In this code, you create an FXMLLoader and point it to the FXMLPassword.fxml file. But when you call loader.load(), which reads the fxml file and creates the UI defined in it, you do nothing with the result. So the UI created by that call to loader.load() is never displayed.
Consequently, when you get the controller from that loader, and use it to make changes to the UI, you will never see those changes because that instance of the TextField is not displayed.
The TextField that is displayed is created when you call
root = FXMLLoader.load(getClass().getResource("FXMLPassword.fxml"));
but since you use the static version of the FXMLLoader.load(...) method here, you have no opportunity to get the controller associated with it.
You need to refactor the code as follows:
#FXML
private void handleBtn1(ActionEvent event) throws Exception{
Info info = new Info(textFieldA.getText(), textFieldB.getText());
Stage stage;
Parent root;
if(event.getSource()==btn1){
//get reference to the button's stage
stage=(Stage) btn1.getScene().getWindow();
//load up OTHER FXML document
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("FXMLPassword.fxml"));
root = loader.load();
FXMLPasswordController passwordController = loader.getController();
passwordController.setCurrentInfo(info);
}
else{
stage=(Stage) btn1.getScene().getWindow();
root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
}
//create a new scene with root and set the stage
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
So I am working on building an application that is kind of like an IDE, think Eclipse or Netbeans. I want to have different zones where users can select something like a view that will show up in one of the zones. My first attempt is to add a console view to on of my zones. In my application class I create the overall UI with the following code:
public void initialize() {
try {
mainController = (MainController) replaceSceneContent("/layout/main.fxml");
}catch (Exception ex) {
Logger.getLogger(MyApplication.class.getName()).log(Level.SEVERE, null, ex);
}
}
private Initializable replaceSceneContent(String fxml) throws Exception {
FXMLLoader loader = new FXMLLoader();
InputStream in = MyApplication.class.getResourceAsStream(fxml);
loader.setBuilderFactory(new JavaFXBuilderFactory());
loader.setLocation(MyApplication.class.getResource(fxml));
AnchorPane page;
try {
page = (AnchorPane) loader.load(in);
} finally {
in.close();
}
Scene scene = new Scene(page);
stage.setScene(scene);
stage.sizeToScene();
return (Initializable) loader.getController();
}
Which results in the following UI
I would then like to add a console widget/view to the southEast quadrant of the application. This will be handled by the MainController. In theory if someone chooses the Console view it will popup in one of the quadrants, but for now I just want to load it at start up. I try to do this with the following code:
public class MainController extends AnchorPane implements Initializable{
#FXML
SplitPane west, east;
#FXML
AnchorPane northWest, southWest, northEast, southEast, applicationHeader;
#FXML
MenuBar menuBar;
Stage stage;
#Override
public void initialize(URL arg0, ResourceBundle arg1) {
try {
ConsoleWidget cw = (ConsoleWidget)loadWidget("/widget/console.fxml");
southEast.getChildren().add(cw);
} catch (Exception e) {
e.printStackTrace();
}
}
private Initializable loadWidget(String fxml) throws Exception {
FXMLLoader loader = new FXMLLoader();
InputStream in = MainController.class.getResourceAsStream(fxml);
loader.setBuilderFactory(new JavaFXBuilderFactory());
loader.setLocation(MainController.class.getResource(fxml));
AnchorPane page;
try {
page = (AnchorPane) loader.load(in);
} finally {
in.close();
}
return (Initializable) loader.getController();
}
}
This should result in the following console appearing in the south east quadrant.
Programatically this seems to work. My southEast anchor pane shows that it has 1 child in its list if you look at it, however my UI doesnt change. I need to know how to refresh my Main controller (or do whatever I need to do) to make the change show up to the user.
I have created applications before where I create an entire new scene as the user goes through different pages but I have never really done something like this where I need to update the scene after adding a node to a pane. I have tried things like stage.sizeToScene() to try to refresh it but that hasnt worked for me.
You must return AnchorPane page instead of (Initializable) loader.getController(); to be added to the southEast anchorPane.
Declaring a Controller which extends another Node, results in a controller-node, which are used for creating Custom Controls.
These controls have constructors to set the Controller and the Root node. See this example
In your case, loader.getController() returns you an AnchorPane(rather ConsoleWidget), which is not loaded from the FXML, but is an instance of the ConsoleWidget class.
I'm using eclipse & scene builder.
I has defined the main window as follow:
I want to define the "Outgoing Messages" tab with X (X is calculated on run time) of elements (the elements defined at different fxml file).
When I'm creating the main window:
private Stage primaryStage;
private AnchorPane rootLayout;
public void start(Stage primaryStage) {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("ABC");
try {
// Load root layout from fxml file.
FXMLLoader loader = new FXMLLoader();
loader.setLocation(MainApp.class.getResource("MainLayout.fxml"));
rootLayout = (AnchorPane) loader.load();
// Show the scene containing the root layout.
Scene scene = new Scene(rootLayout);
primaryStage.setScene(scene);
primaryStage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
How can I update the "Outgoing Messages" tabs as I described above ?
Thanks
You may use the <fx:include> inside your FXML for Outgoing Messages. This will automatically load X number of fxml before loading the main FXML. After all the incude FXML's are loaded, the FXML for Outgoing Messages is prepared and shown.
Update
If you have dynamic increase/decrease in elemtents, use the initialize() of the Outgoing Messages to populate the elements inside it instead of using <fx:incude>.
Lets consider (you may have your own logic) your Outgoing Messages has a VBox which is to be filled with dynamic no of elements(BorderPane in this case) which is inside X.fxml (You may have different FXML or you can create you own control at runtime and add it) :
pubic class OutgoingMessagesController implements Initializable {
#FXML
private VBox vbox;
public void initialize(java.net.URL location, java.util.ResourceBundle resources) {
//Whatever your logic is, I am considering a dynamic number here
for(int i=0; i<dynamicNumber; i++){
BorderPane borderPane = FXMLoader.load(getClass().getResource("X.fxml"));
vbox.getChildren.add(borderPane);
}
}
}
I am new to JavaFX. I have a window loaded with vertical split pane. Here left side of the split page I have couple of buttons. On Each button click I need to load separate fxml on the right side of the split pane. So here I paste screen shot to be clear.
Here as of now I am opening in separate stage, separate scene when search button is hit. Now I need to load Searcher in the right side of baselayout window. Here is some code which loads baseLayout.
#Override
public void start(Stage primaryStage) throws Exception {
this.primaryStage = primaryStage;
this.primaryStage.setTitle("Base Layout");
BaseController.setMlTool(this);
FXMLLoader loader = new FXMLLoader(MLTool.class.getResource("view/Base.fxml"));
baseLayout = (AnchorPane) loader.load();
Scene scene = new Scene(baseLayout);
primaryStage.setScene(scene);
primaryStage.show();
}
Here is some code which loads Searcher on button click.
#FXML
private void initialize(){
System.out.println("Testing");
}
#FXML
private void handleSearchButton(){
System.out.println("Handle Button Called");
Stage search = new Stage();
FXMLLoader loader = new FXMLLoader(MLTool.class.getResource("view/Searcher.fxml"));
search.setTitle("Searcher");
try {
AnchorPane searcherPage = (AnchorPane) loader.load();
Scene scene = new Scene(searcherPage);
search.setScene(scene);
search.show();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Here how do I load Searcher in BaseLayout's Rightside of split pane.
Hope my question is clear. Thanks for your help in anticipation.
You can take pane on right side and then load fxml as child node of that pane. Stage is same for all time and you can load fxml in pane.
ex: MainWindow.fxml is perent window. You can load in stage.
Parent root = FXMLLoader.load(getClass().getResource("MainWindow.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
Now in MainWindow.fxml you have split pane. Put anchorPan on right side where you want to load fxml on button click. Then add fxml as child in anchorpane.
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(fxml));
Pane cmdPane = (Pane) fxmlLoader.load();
try {
anchCmdController.getChildren().clear();
anchCmdController.getChildren().add(cmdPane);
fadeIn.playFromStart();
} catch (Exception e) {
e.printStackTrace();
}
try to listen to the button click, once its clicked use 'FXMLLoader.load' to load based on the root elenment, typecast it say panel and assign tot he content of the right side.
In Your handler method try with this:
first declare anchorpane. In fxml right pane put anchorpane with anchCmdController name.
#FXML
private AnchorPane anchCmdController;
then update your handleSearchButton event.
#FXML
private void handleSearchButton() throws IOException {
System.out.println("Handle Button Called");
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("view/Searcher.fxml"));
Pane cmdPane = (Pane) fxmlLoader.load();
try {
/* NEED TO DECLARE ANCHOR PANE" */
anchCmdController.getChildren().clear();
anchCmdController.getChildren().add(cmdPane);
} catch (Exception e) {
e.printStackTrace();
}
}