load fxml into center of body pane of another fxml - javafx

I am trying to call an fxml (test.fxml) into another fxml (main.fxml) in center of body pane. I am actually doing this in the controller class of main.fxml. No matter how many different ways I try, I am just not able to set the center element at all.
Requirement is, I need to display different fxml files on right click of tree structure in main fxml file.
but when I call this method, I get the exception 'location is required'. Is there something I need to do? Please help.
main.fxml
#FXML
TreeView<String> mainTree;
#FXML
BorderPane rootLayout;
#FXML
AnchorPane dynamicContent;
ContextMenu cntxtMenu;
//Method where i am trying to set main.fxml center body
public void showAttributeScreen(){
FXMLLoader loader = new FXMLLoader(scriptbuilder.controller.TestController.class.getResource("view/test.fxml"));
AnchorPane pane = new AnchorPane();
try{
pane = (AnchorPane) loader.load();
rootLayout.setCenter(pane);
}
catch(Exception e){
}
}
test.fxml
<AnchorPane id="AnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" fx:controller="scriptbuilder.controller.TestController">
<Label alignment="BASELINE_CENTER" text="Hi" />
</AnchorPane>

Related

Assigning FXML elements in controller from a newly added pane - JavaFX

Let's assume, for simplicity's sake, I have an FXML with a SplitPane. On one side I have a Button; the other has an empty AnchorPane (for now).
The button simply places a pane on the other side of the SplitPane from another FXML file. The newly added pane contains a Button and a Label. Both FXMLs have the same controller for two reasons: 1. The Added Pane FXML is not used elsewhere, 2. The Added Pane FXML has so little functions that it's not really worth it to have a separate controller for it.
Pressing the button on the newly added pane sets the text in its label to "Hello World".
Split Pane
<SplitPane fx:controller="myController".... >
<items>
<VBox .... >
<Button fx:id="mainButton" onAction="#handleFirstButton".... />
</VBox>
<AnchorPane fx:id="secondPane" ....>
</items>
</SplitPane>
Added Pane
<VBox fx:controller="myController" ....>
<Button fx:id="btn" onAction="#changeText" ... />
<Label fx:id="lbl" .... />
</VBox>
Controller Class
// Regular stuff here
#FXML
AnchorPane secondPane;
#FXML
Button mainButton;
#FXML
Button btn;
#FXML
Label lbl;
public void initialize(URL arg0, ResourceBundle arg1) {
}
private void loadSecondPane {
FXMLLoader loader = FXMLLoader.load(getClass().getResource("AddedPane.fxml"));
VBox box = loader.load();
secondPane.setTopAnchor(box);
}
#FXML
private void handleFirstButton(ActionEvent event) {
loadSecondPane();
}
#FXML
private void changeText(ActionEvent event) {
lbl.setText("Hello World");
}
The problem is, when the Added Pane is loaded, its components will NOT be defined in the controller, and trying to perform any method on them will produce a NullPointerException.
Is there a way around this or is having a separate controller mandatory in this case? And assuming I have 2+ buttons on the first side of the pane, and each button produces a different pane on the other side, does the solution remain the same?
NOTE: I'm fairly new with javafx, so excuse any mistypes.

How to change parent of child fxml into other parent?

Can I change a Node's parent to another parent in FXML?
Suppose a parent named stackcon with a child node.
I now want to move this child node to different parent, e.g., named stackmain.
Is this possible? If yes, please give me a link or a example code.
This is just one of many ways how to do this.
MainView.fxml, just a simple view containing a button, and on button click the label should be moved from right to left and vice-versa (see the onMousePressed declaration):
<fx:root type="BorderPane" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1">
<left>
<VBox fx:id="lefty">
<Label fx:id="switchy" text="Switching text"></Label>
</VBox>
</left>
<center>
<Button fx:id="switchBtn" text="Switch" onMousePressed="#switchButtonPressed"></Button>
</center>
<right>
<VBox fx:id="righty">
</VBox>
</right>
</fx:root>
The controller MainView.java with its switchButtonPressed event handler:
public class MainView extends BorderPane {
#FXML private VBox lefty;
#FXML private VBox righty;
#FXML private Label switchy;
public MainView() {
URL fxmlFile = MainView.class.getResource("MainView.fxml");
FXMLLoader fxmlLoader = new FXMLLoader(fxmlFile);
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException e) {
e.printStackTrace();
}
}
public void switchButtonPressed(MouseEvent mouseEvent) {
if(lefty.getChildren().contains(switchy)) {
lefty.getChildren().remove(switchy);
righty.getChildren().add(switchy);
} else {
righty.getChildren().remove(switchy);
lefty.getChildren().add(switchy);
}
}
}
You see, I just check on button click if the label is on the left side. If so, remove it on the left side and add it on the right side.
Analogous, if it is on the right side, remove it there and add it on the left side.
it may vary depending on type of the pane of stackmain & stackcon , i'm assuming that you use AnchorPane
but it is something like this
Node childNode = stackcon.getChildren().get(indexOfChild);
stackcon.getChildren().remove(indexOfChild);
stackmain.getChildren().add(childNode);

Javafx8 stackpane children blocking mouse events

I have a javafx8 app with a complex scene. It consists of a splitpane where, in the top segment I have a stackpane with multitple levels (children). The rear most is a background that would only need redraws on resizing, another with gridlines, lat/long. Another for drawing and, finally, another with a sliding composite control. When the composite (buttons, labels, comboboxes, and textfields, in the last child of the stackframe, it properly intercepted and acted on mouse and keyboard events. However, with it on top no events were getting propagated to the child panes below. I switched the order and now the buttons and axis control work but nothing now gets propagated to the composite control below it. Buttons fail to see the mouse action.
This seems similar to Stackpane - MouseClick to be listened by both its children but it's not real clear that it is nor is it clear that has a correct answer.
I simplified my scenario with a very simple test, source to follow. In this I have a StackPane with two children, both VBox, one if which with top left alignment, the other with top right. Only the second button, top right responds to being pressed.
Why. The reason I have my app with multiple panes is performance. Both drawing actions are expensive and freq and I didn't want to have to constantly redraw static or near static layouts.
fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<StackPane fx:id="containerPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="mouseeventdemo.MouseEventDemoController">
<children>
<VBox fx:id="container1" prefHeight="200.0" prefWidth="100.0">
<children>
<Button fx:id="leftButton" mnemonicParsing="false" onAction="#leftButtonDown" text="left button" />
</children>
</VBox>
<VBox fx:id="container2" alignment="TOP_RIGHT" prefHeight="200.0" prefWidth="100.0">
<children>
<Button fx:id="rightButton" mnemonicParsing="false" onAction="#rightButtonDown" text="Right Button" />
</children>
</VBox>
</children>
</StackPane>
Controller:
package mouseeventdemo;
/**
* Sample Skeleton for 'FXMLDocument.fxml' Controller Class
*/
import java.net.URL;
import java.util.ResourceBundle;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
public class MouseEventDemoController {
#FXML // ResourceBundle that was given to the FXMLLoader
private ResourceBundle resources;
#FXML // URL location of the FXML file that was given to the FXMLLoader
private URL location;
#FXML // fx:id="containerPane"
private StackPane containerPane; // Value injected by FXMLLoader
#FXML // fx:id="container1"
private VBox container1; // Value injected by FXMLLoader
#FXML // fx:id="leftButton"
private Button leftButton; // Value injected by FXMLLoader
#FXML // fx:id="container2"
private VBox container2; // Value injected by FXMLLoader
#FXML // fx:id="rightButton"
private Button rightButton; // Value injected by FXMLLoader
#FXML
void leftButtonDown(ActionEvent event) {
System.out.println("leftButtonPressed");
}
#FXML
void rightButtonDown(ActionEvent event) {
System.out.println("rightButtonPressed");
}
#FXML // This method is called by the FXMLLoader when initialization is complete
void initialize() {
assert containerPane != null : "fx:id=\"containerPane\" was not injected: check your FXML file 'FXMLDocument.fxml'.";
assert container1 != null : "fx:id=\"container1\" was not injected: check your FXML file 'FXMLDocument.fxml'.";
assert leftButton != null : "fx:id=\"leftButton\" was not injected: check your FXML file 'FXMLDocument.fxml'.";
assert container2 != null : "fx:id=\"container2\" was not injected: check your FXML file 'FXMLDocument.fxml'.";
assert rightButton != null : "fx:id=\"rightButton\" was not injected: check your FXML file 'FXMLDocument.fxml'.";
}
}
main
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package mouseeventdemo;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
/**
*
* #author walt
*/
public class MouseEventDemo 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);
}
}
In my app the rear most pane is a background canvas. It need be drawn but once and redrawn when the window is resized.
The next is a canvas that had lat/long grids. It need only be redrawn when the window size changes or either the vertical or horizontal scales change.
The next is the main drawing canvas in the following
<vbox>
<hbox>
drawing canvas
vertical scale
</hbox>
horizontal scale
</vbox>
The next carries one or more flagpoles where the vertical pole is a slide rule like reticle. These need to be dragable. If this layer is on top it slides perfectly but the two scales fail to get focus. If the top two panes are reversed, the scales work perfectly but the flagpole objects never receive events.
The two button example duplicates very simply what I see.
Thanks!
In the eventhandling of javafx only the topmost node (in your example container2) under the mouseposition will be determined as the target for the mouse click event, that's why container1 doesn't receive the event.
The target node is the end node in the event dispatch chain. When you click on leftButton, the mouse clicked event travels from the root node of your scene graph to the target node(container2), and back if it doesn't get consumed. Container1 as not being on the dispatch chain, won't receive the event:
http://docs.oracle.com/javafx/2/events/processing.htm
You can try to use container.setPickOnBounds(false)

javaFX cannot access Scene elements

I am new to javafX, have run through various tutorials, and googled extensively. I am attempting to write a multiscreen javaFX program whose second screen has a split pane, whose right pane should display a grid and a button. The framework I am using for multiple screens is copied form Angela Caiedo's MultipleScreens Framework tutorial ( https://github.com/acaicedo/JFX-MultiScreen/tree/master/ScreensFramework) .
What works: The screens framework is successful in terms of flipping between my multiple screens.
My problem: I click the button on screen 1 to move to screen 2. Screen 2 successfully shows. On screen 2 I click a button to populate data in my vBox. The code in my controller class for screen 2 is unable to access the vbox (or any other defined containers on this screen).
My main class sets up the controller screens.
public class FieldMapCreator extends Application {
public static String mainID = "main";
public static String mainFile = "MainMapFXMLDocument.fxml";
public static String initialMapID = "initialMap";
public static String initialMapFile = "FXMLMapPicture.fxml";
#Override
public void start(Stage stage) throws Exception {
ScreensController mainContainer = new ScreensController();
mainContainer.loadScreen(FieldMapCreator.mainID, FieldMapCreator.mainFile);
mainContainer.loadScreen(FieldMapCreator.initialMapID, FieldMapCreator.initialMapFile);
mainContainer.setScreen(FieldMapCreator.mainID);
Group root = new Group();
root.getChildren().addAll(mainContainer);
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
}
I get to the second screen by clicking a button on screen 1. Button1 code looks like:
#FXML
private void initialMapButtonAction(ActionEvent event) {
myController.setScreen(FieldMapCreator.initialMapID);
}
Screen 2 has a button that when clicked executes a method that creates data for the vbox. The fxml for screen 2 is this:
<AnchorPane id="AnchorPane" fx:id="mainAnchorPane" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="FieldMapCreator.FXMLMapPictureController">
<children>
<SplitPane fx:id="mapSplitPane" dividerPositions="0.29797979797979796" layoutX="3.0" layoutY="7.0" prefHeight="400.0" prefWidth="600.0">
<items>
<AnchorPane fx:id="anchorPaneLeft" minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
<children>
<Button fx:id="backButton" layoutX="14.0" layoutY="348.0" mnemonicParsing="false" onAction="#goToMainScreen" text="Back" />
</children>
</AnchorPane>
<AnchorPane fx:id="anchorPaneRight" minHeight="0.0" minWidth="0.0" prefHeight="364.0" prefWidth="401.0">
<children>
<Button fx:id="vBoxShowMap" layoutX="24.0" layoutY="346.0" mnemonicParsing="false" onAction="#buildMapFromNurseries" prefHeight="26.0" prefWidth="91.0" text="show map" />
<VBox fx:id="mapVBox" layoutX="24.0" layoutY="24.0" onMouseClicked="#vBoxMouseClicked" prefHeight="200.0" prefWidth="309.0" />
</children></AnchorPane>
</items>
</SplitPane>
</children>
</AnchorPane>
The code that fails is in the "showInitialMap()" method of the controller class for screen 2. When executed, the last line of this method "mapVBox.getChildren().add(root)", results in a null pointer exception. I expected this to be resolved via the fxml injection. I also tried the commented out code to retrieve "mapVBox" using scene lookup, but it also results in a null pointer exception. The controller code is below:
public class FXMLMapPictureController implements Initializable, ControlledScreen {
ScreensController myController;
static MyNode[][] mainField ;
#FXML
private AnchorPane mainAnchorPane;
#FXML
private static SplitPane mapSplitPane;
#FXML
private AnchorPane anchorPaneLeft;
#FXML
private static AnchorPane anchorPaneRight;
#FXML
private static VBox mapVBox;
#FXML
private Button vBoxShowMap;
/**
* Initializes the controller class.
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
// Should something be in here ??
}
public void goToMainScreen() {
myController.setScreen(FieldMapCreator.mainID);
}
public void showInitialMap() {
int row = userFieldMap.getNumRows();
int col = userFieldMap.getNumCols();
double gridWidth = 309/col;
double gridHeight = 200/row;
Group root = new Group();
// initialize the field
for (int idx = 0; idx < col; idx++) {
for (int jdx = 0; jdx < row; jdx++) {
// create plot
Plot plot = new Plot(idx, jdx, "Plot" + idx + "/" + jdx);
// Create node to hold the Plot
MyNode node = new MyNode(idx*gridWidth, jdx*gridHeight, gridWidth, gridHeight, plot);
// add plot to group
root.getChildren().add(node);
}
}
//Scene myScene = myController.getScene();
//VBox mapVBox = (VBox)myScene.lookup("#mapVBox");
mapVBox.getChildren().add(root);
}
#Override
public void setScreenParent(ScreensController screenParent) {
myController = screenParent;
}
Is there something missing from the initialize() method? I'm wondering if I am not setting something correctly when I change screens. I am able to access the initial screen's containers without problem from all screen 1 methods including the initialize class. When I attempt the same in screen 2, I get the message "screen hasn't been loaded !!!"
Thanks for any help you can provide.
You are using static fields as FXML injection targets which don't work in Java 8 and should not have been implemented to work in any other version, as static UI elements just don't make sense. Here's a bit more detailed answer. Having resorted to a static field most of the time means the software design of the application needs to be reconsidered.

Why won't a child window (child stage) contents auto-resize?

I'm working in NetBeans 8 with Java 8/JavaFX.
I have an application that starts a main stage based on an fxml file.
There is a menu option for a user to bring up a second stage on request. The function that opens the window looks like this:
#FXML
private void openChildWindow(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();
}
The content of second.fxml looks like this (after all the import statements):
<AnchorPane id="AnchorPane" prefHeight="680.0" prefWidth="1020.0" xmlns:fx="http://javafx.com/fxml/1" >
<stylesheets><URL value="#css/mycss.css" /></stylesheets>
<children>
<TabPane AnchorPane.leftAnchor="0.0" AnchorPane.topAnchor="10.0"
AnchorPane.bottomAnchor="10.0" AnchorPane.rightAnchor="0.0" >
<Tab text="A" closable="false"></Tab>
</TabPane>
</children>
</AnchorPane>
The problem: when I click on the corner of the second window to drag and resize it, the contents are not resizing. What am I missing or what do I need so that it will auto-resize?
Group is not directly resizable as stated in its javadoc. So it does not being resized by the scene while the window is resized from the corner. You can use the subclasses of Pane instead of. For example try with
VBox root = new VBox();
Instead of using a Group, please put your AnchorPane directly into your Scene
#FXML
private void openChildWindow(ActionEvent event) throws Exception {
Stage stage = new Stage();
AnchorPane frame = FXMLLoader.load(getClass().getResource("second.fxml"));
Scene scene = new Scene(frame );
stage.setScene(scene);
stage.show();
}
A Group cannot be resized, "A Group will take on the collective bounds of its children and is not directly resizable." http://docs.oracle.com/javafx/2/api/javafx/scene/Group.html
Replace Group with a different of layout such as StackPane, or BorderPane.
I struggled with the same thing, if we only had read the documentation. :D

Resources