Relevant Files: (apologies on any formatting, made many attempts to make it work)
If the files are not sufficient, the repository is here: https://github.com/TheeNinja/StockLookupTool
stock_lookup_tool_main.fxml
<BorderPane fx:id="borderPane" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.112" xmlns:fx="http://javafx.com/fxml/1" fx:controller="me.theeninja.stocklookuptool.gui.StockLookupToolApplicationController">
<top>
...
</top>
<left />
<center />
</BorderPane>
stock_information_center.fxml
<GridPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="me.theeninja.stocklookuptool.gui.selection.StockSearchSelectionController"
prefHeight="400.0" prefWidth="600.0"
fx:id="stockInformationCenter">
</GridPane>
favorite_stocks_sidebar.fxml
<VBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="me.theeninja.stocklookuptool.gui.selection.StockSearchSelectionController" fx:id="verticalStockList">
...
</VBox>
StockLookupTool.java (Class with the start method)
public class StockLookupTool extends Application {
....
#Override
public void start(Stage stage) throws Exception {
...
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("/fxml/stock_lookup_tool_main.fxml"));
Parent root = loader.load();
Scene scene = new Scene(root, 250, 300);
stage.setTitle(APPLICATION_TITLE);
stage.setScene(scene);
stage.setFullScreen(true);
stage.show();
}
....
}
StockLookupToolApplicationController (controller for stock_lookup_tool_main.fxml)
public class StockLookupToolApplicationController {
...
#FXML public Label stockSearchLabel;
#FXML public Label newsLabel;
#FXML public Label settings;
#FXML public BorderPane borderPane;
#FXML public HBox topApplicationNavigation;
#FXML
public void handleStockSearchSelection() {
logger.log(Level.INFO, "Setting GUI View to Stock Search");
setView(StockSearchSelectionController.getInstance());
}
private void setView(Selection selection) {
borderPane.setLeft(selection.getLeft());
borderPane.setCenter(selection.getCenter());
borderPane.setRight(selection.getRight());
borderPane.setBottom(selection.getBottom());
}
}
StockSearchSelectionController.java (controller for both stock_information_center.fxml and favorite_stocks_sidebar.fxml)
public class StockSearchSelectionController implements Selection {
#FXML public Label favoriteStocksLabel;
#FXML public TextField addFavoriteStockInput;
#FXML public VBox verticalStockList;
#FXML public HBox addFavoriteStockInputContainer;
#FXML public GridPane stockInformationCenter;
#FXML
public void handleFavoriteStockInput(KeyEvent keyEvent) {
if (keyEvent.getCode() == KeyCode.ENTER) {
...
// makes visual changes to both stockInformationCenter AND verticalStockList, hence I need access to both fxml files (which is why this controls both).
}
}
#Override
public Node getLeft() {
return verticalStockList;
}
#Override
public Node getCenter() {
return stockInformationCenter;
}
}
More information:
The interface Selection has the methods getLeft() and getCenter() (among other irrelevant ones). Both of these returns Node. In stock_lookup_tool_main.fxml, in the top portion of the BorderPane I have a button (cut out in the provided snippet) that, when pressed, calls handleStockSearchSelection() (this method call indeed happens, I have verified it with a logger).
Now there is a controller, that controls both stock_information_center.fxml and favorite_stocks_sidebar.fxml. This controller implements Selection, and in turn, implements getLeft() and getCenter(). getLeft() returns the VBox variable
that correlates to favorite_stocks_sidebar.fxml, while getCenter() returns the GridPane variable that correlates to stock_information_center.fxml. When handleStockSearchSelection() is called, the left part of the borderPane object is set to the VBox, and the center is set to the GridPane. All these method calls/actions have been verified to occur. However, there is no visual change to the scene.
My question is: Why are these changes to borderPane not implemented visually in the scene? I have established a link between the scene <-> stock_lookup_tool_main.fxml through setting the loader's location. I have also established a link between stock_lookup_tool_main.fxml <-> its controller, hence they should share changes. I have modified the border pane in stock_lookup_tool_main.fxml in the controller by calling setLeft() and setCenter() on the variable (same name as the ID, borderPane). Yet, the scene visually does not change.
Related
This question already has answers here:
How to Fix NoClassDefFoundError in JavaFX?
(1 answer)
I'm encounter a java.lang.NoClassDefFoundError and I don't know why it's occuring [duplicate]
(1 answer)
Closed 5 months ago.
I am working on a JavaFX project, and I am having trouble displaying the user interface. I keep getting this error:
Error: Could not find or load main class Main.SongApp
Caused by: java.lang.NoClassDefFoundError: javafx/application/Application
I am confused because my directory has no files named 'application' or 'Application.'
I have these three files and I am not sure why this code does not run. This is the main file that I try to display the user interface from:
SongApp.java
public class SongApp extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
// create FXML loader
FXMLLoader loader = new FXMLLoader();
loader.setLocation(getClass().getResource("/SongLibraryProject/ControllerTing/SongLib.fxml"));
// load fxml, root layout manager in fxml file is GridPane
AnchorPane root = (AnchorPane)loader.load();
// set scene to root
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setResizable(false);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
This file has my front end code from Scene Builder and the anchor table I am trying to display: SongLib.fxml
<AnchorPane minHeight="400.0" minWidth="747.0" prefHeight="400.0" prefWidth="747.0" style="-fx-background-color: #263238;" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="SongLibrary.View.Controller">
//front end(scenebuilder code)
</AnchorPane>
This is the the controller file: SongLibController.java
public class SongLibController {
#FXML ListView<Song> songList;
#FXML Button add;
#FXML Button edit;
#FXML Button delete;
#FXML Text name;
#FXML Text artist;
#FXML Text album;
#FXML Text year;
#FXML TextField nameField;
#FXML TextField artistField;
#FXML TextField albumField;
#FXML TextField yearField;
private ObservableList<String> obsList;
public void add(ActionEvent e) {
System.out.println("add");
}
public void edit(ActionEvent e) {
System.out.println("edit");
}
public void delete(ActionEvent e) {
System.out.println("delete");
}
}
I am trying to update the center node of a BorderPane using FXML. I have created a VBox on the left with Buttons and when clicked they are supposed to update the center node however the center node does not update. Can someone shed some light on what I am doing incorrectly?
I know the buttons events work (they print to the screen usiong println) but it will not update the UI.
The root nodes .fxml (the BorderPane):
<?import javafx.scene.layout.BorderPane?>
<BorderPane xmlns:fx="http://javafx.com/fxml" fx:id="rootPane">
<left>
</left>
<center>
</center>
</BorderPane>
The menu
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Button?>
<VBox fx:controller="" xmlns:fx="http://javafx.com/fxml" >
<children>
<Button text="Dashboard" fx:id="dashboardButton" onAction="#dashboardButtonEvent" />
<Button text="Sales" fx:id="salesButton" onAction="#salesButtonEvent" />
</children>
</VBox>
The MenuController
public class MenuController implements Initializable {
#FXML
private BorderPane rootPane;
#FXML
private Button dashboardButton; //same name as in the menu.fxml file
#FXML
private Button salesButton; //same name as in the menu.fxml file
#FXML
private void dashboardButtonEvent(ActionEvent event) {
try {
VBox dashboard = FXMLLoader.load(getClass().getResource("../../../fxml/dashboard.fxml"));
rootPane = FXMLLoader.load(getClass().getResource("../../../fxml/root.fxml"));
rootPane.setCenter(dashboard);
} catch (IOException e) { System.out.println(e); }
}
#FXML
private void salesButtonEvent(ActionEvent event) {
try {
VBox sales = FXMLLoader.load(getClass().getResource("../../../fxml/sales.fxml"));
rootPane = FXMLLoader.load(getClass().getResource("../../../fxml/root.fxml"));
rootPane.setCenter(sales);
} catch (IOException e) { System.out.println(e); }
}
#Override
public void initialize(URL url, ResourceBundle resourceBundle) {
}
}
I assume where I am going wrong is the way I am going about assigning the root pane using rootPane = FXMLLoader.load(getClass().getResource("../../../fxml/root.fxml"));. If thats the case, how do I get the current rootPane from the existing scene?
How would you implement a full screen feature that can be toggled by pressing F11?
You can add an EventHandler to the primaryStage where you specify the functionality like:
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("View.fxml"));
AnchorPane pane = loader.load();
primaryStage.setScene(new Scene(pane, 400, 400));
primaryStage.addEventHandler(KeyEvent.KEY_PRESSED, event -> {
if (KeyCode.F11.equals(event.getCode())) {
primaryStage.setFullScreen(!primaryStage.isFullScreen());
}
});
primaryStage.show();
}
}
Just to be complete:
View.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane xmlns="http://javafx.com/javafx"
xmlns:fx="http://javafx.com/fxml"
fx:controller="stackoverflow.testfullscreen.Controller">
</AnchorPane>
Controller:
public class Controller implements Initializable {
#Override
public void initialize(URL location, ResourceBundle resources) {
}
}
I didn't implement a webview, but it should work with any scene.
I have two ToggleButtons and I would like them both to be bound to a Boolean property so that when one ToggleButton is selected, the other ToggleButton isn't, and the BooleanProperty is true, and vice versa.
Here's what I tried.
FXML file:
<VBox xmlns:fx="http://javafx.com/fxml" fx:controller="my.package.MainController" styleClass="Tool" fx:id="root">
<HBox spacing="10">
<fx:define>
<ToggleGroup fx:id="modeToggleGroup"/>
</fx:define>
<ToggleButton fx:id="manualModeBtn" text="Manual Mode" selected="true" toggleGroup="$modeToggleGroup"/>
<ToggleButton fx:id="automaticModeBtn" text="Automatic Mode" toggleGroup="$modeToggleGroup"/>
</HBox>
<!-- other stuff -->
</VBox>
Controller file:
public class MainController {
#FXML
private ToggleButton manualModeBtn;
#FXML
private ToggleButton automaticModeBtn;
private BooleanProperty isAutomaticMode;
public void initialize() {
isAutomaticMode = new SimpleBooleanProperty();
automaticModeBtn.selectedProperty.bindBidirectional(isAutomaticMode);
}
}
The ToggleGroup ensures that neither button is selected at the same time, but I can still unselect both of them, which I don't want to be possible.
How do I bind the other ToggleButton to the opposite (i.e. not()) of the Boolean Property?
You could do this by using listeners:
public class MainController {
#FXML
private ToggleButton manualModeBtn;
#FXML
private ToggleButton automaticModeBtn;
public void initialize() {
manualModeBtn.selectedProperty().addListener((obs, wasSelected, isNowSelected) -> automaticModeBtn.setSelected(! isNowSelected));
automaticModeBtn.selectedProperty().addListener((obs, wasSelected, isNowSelected) -> manualModeBtn.setSelected(! isNowSelected));
}
}
Note that RadioButton has almost this functionality already (in the case of a radio button, you can't "deselect" it), so you could simply use RadioButtons instead. Note this question, if it is just a case of how they look.
This can also be done by setting a listener on the ToggleGroup's selectedToggleProperty.
You first have to set an initial button.
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class ToggleButtonExperiments extends Application
{
#Override
public void start(Stage primaryStage) throws Exception
{
primaryStage.setTitle("HBox Experiment 1");
ToggleButton toggleButton1 = new ToggleButton("Left");
ToggleButton toggleButton2 = new ToggleButton("Right");
ToggleGroup toggleGroup = new ToggleGroup();
toggleButton1.setSelected(true);//set initial button!!!!!
toggleGroup.selectedToggleProperty().addListener((obs, oldTog, newTog) -> {
if (newTog == null) {
oldTog.setSelected(true);
}
});
toggleButton1.setToggleGroup(toggleGroup);
toggleButton2.setToggleGroup(toggleGroup);
HBox hbox = new HBox(toggleButton1, toggleButton2);
Scene scene = new Scene(hbox, 200, 100);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args)
{
Application.launch(args);
}
}
I am new to JavaFX and trying to create an Confirmation Dialogbox.
I know already that there is no real build in dialogbox in JavaFX so I created one myself like this:
#FXML
public void delBox() {
try {
Stage dialogStage = new Stage();
AnchorPane root = FXMLLoader.load(getClass().getResource("Dialog.fxml"));
Scene scene = new Scene(root);
dialogStage.setScene(scene);
dialogStage.showAndWait();
} catch(Exception e) {
e.printStackTrace();
}
}
It looks pretty good already, but what I dont understand is, how those two Stages can communicate with each other? I want to pass a String to the dialog which is than shown in the message, also when one of the buttons in the dialog window is clicked I wanna react to this in the accordingly.
Can anyone explain me how communication between the stages works?
btw: I use .FXML files and controller classes.
You need a reference to the controller for the dialog. To do this, create an instance of FXMLLoader instead of using the static FXMLLoader.load(URL) method.
For example, suppose you have a class DialogController, so your Dialog.fxml looks like:
<AnchorPane xmlns:fx="..." fx:controller="DialogController.fxml">
...
</AnchorPane>
Then you can access the DialogController in the delBox() method above with
Stage dialogStage = new Stage();
FXMLLoader loader = new FXMLLoader(getClass().getResource("Dialog.fxml"));
AnchorPane root = (AnchorPane)loader.load();
DialogController controller = (DialogController) loader.getController();
Scene scene = new Scene(root);
dialogStage.setScene(scene);
dialogStage.showAndWait();
And now you can communicate between the two controllers. For example, in DialogController you could define a message property, and bind it to a Label :
public class DialogController {
private final StringProperty message = new SimpleStringProperty("");
public void setMessage(String message) {
this.message.set(message);
}
public String getMessage() {
return message.get();
}
public StringProperty messageProperty() {
return message ;
}
#FXML
private Label label ;
public void initialize() {
label.textProperty().bind(message);
// ...
}
}
And then back in your delBox() method:
//... as before:
AnchorPane root = (AnchorPane)loader.load();
DialogController controller = (DialogController) loader.getController();
controller.setMessage("Hello World");
// ...
Similarly, you can define properties which are set when controls are pressed in the dialog itself, and either observe them or query them after the showAndWait() call.
There are a bunch of other similar techniques. Some examples:
https://github.com/james-d/Shared-Data-Controller/tree/master/src
https://github.com/james-d/Dialog-FXML-Example/tree/master/src
https://github.com/james-d/Nested-Controller-Example/tree/master/src/nestedcontrollerexample
<AnchorPane xmlns:fx="..." fx:controller="DialogController.fxml">
...
</AnchorPane>
FX Controller is a java file, so it has to be DialogController and the Controller's path should be included i.e, fx:controller="applicationPackageName.DialogController"
The above mentioned fxml code does not work. It results in
javafx.fxml.LoadException
java.lang.InstantiationException
java.lang.NoSuchMethodException
Reason: The jvm looks for a class constructor with 0 parameters to build an instance. To overcome the error, the controller file needs to be loaded in the function coded in java:
loader.setController(new ControllerName(""));
To sum up (Working code):
FXML file:
<BorderPane id="background" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="240.0" prefWidth="320.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" >
<bottom>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0">
<children>
<Button onAction="#close" text="OK" />
</children>
</HBox>
</bottom>
<center>
<Label fx:id="messageLabel" />
</center>
</BorderPane>
Controller file:
public class PiPreferenceController {
private final String message ;
#FXML
private Label messageLabel ;
#FXML
void initialize() {
messageLabel.setText(message);
}
public PiPreferenceController(String message) {
this.message = message ;
}
#FXML
public void close() {
messageLabel.getScene().getWindow().hide();
}
}
Function:
void dialogPreferences() throws IOException {
Stage dialogStage = new Stage();
FXMLLoader loader = new FXMLLoader(getClass().getResource(
"PiPreference.fxml"));
loader.setController(new PiPreferenceController(""));
BorderPane root = (BorderPane) loader.load();
Scene scene = new Scene(root);
dialogStage.setScene(scene);
dialogStage.showAndWait();
}