custom FXML component not loading - classnotfoundexception - java

I have followed tutorials and created my own custom element. It is an AnchorPane containing 6 overlapped canvases to create a graphing framework.
Below is the magnitudeGraph.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.canvas.Canvas?>
<?import javafx.scene.layout.AnchorPane?>
<fx:root type="javafx.scene.layout.AnchorPane" minHeight="200.0" minWidth="700.0" prefHeight="300.0" prefWidth="900.0" xmlns="http://javafx.com/javafx/9" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Canvas fx:id="backgroundCanvas" height="300.0" width="900.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
<Canvas fx:id="series1Canvas" height="300.0" width="900.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
<Canvas fx:id="series2Canvas" height="300.0" width="900.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
<Canvas fx:id="series3Canvas" height="300.0" layoutX="7.0" width="900.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
<Canvas fx:id="series4Canvas" height="300.0" width="900.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
<Canvas fx:id="savedSeriesCanvas" height="300.0" layoutY="-1.0" width="900.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children>
</fx:root>
Fairly Simple. I then have a custom controller which loads this framework, called magnitudeGraphController
public class magnitudeGraphController extends AnchorPane implements Initializable {
#FXML
public Canvas backgroundCanvas;
#FXML
public Canvas series1Canvas;
#FXML
public Canvas series2Canvas;
#FXML
public Canvas series3Canvas;
#FXML
public Canvas series4Canvas;
#FXML
public Canvas savedSeriesCanvas;
public magnitudeGraphController(){
System.out.println("Graph Initialised");
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("magnitudeGraph.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
Nice and easy. So then on my main application controller I can call an instance of magnitudeGraph and the controller will load the fxml file with the root element being the anchorPane.
This is called with the following method in the main app:
public magnitudeGraphController spectrumgraph = new magnitudeGraphController();
Now the difficult thing comes when writing the fxml for my main app. I wrote the import for the controller:
<?import myApp.magnitudeGraphController?>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0">
<children>
<magnitudeGraphController fx:id="spectrumgraph" layoutX="38.0" layoutY="33.0" minHeight="200.0" minWidth="700.0" prefHeight="300.0" prefWidth="944.0">
</AnchorPane>
What I get is an error on the fxml editor stating 'Class javafx.scene.layout.AnchorPane' does not support property MagnitudeGraphController'.
What is confusing me is that this was working perfectly. All I have done is changed the name of the fxml file and controller to a more identifiable name and have refactored the file to reflect this. I have gone back through my archived backup of when this was working and cannot find any difference except the file names. If I try and compile the error throws classnotfoundexception when trying to load magnitudeGraphController.
I have been tearing my hair out for ages and cannot find what has brought the whole thing down.

Related

Components won't auto-resizing when using JFXDecorator, how to fix that?

I want to use JFXDecorator to fancy a look of a Window of my application. But using it brokes an auto-resize of components. This is what my code looks like
#Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("ApplicationView.fxml"));
Parent root = loader.load();
JFXDecorator decorator = new JFXDecorator(stage, root);
decorator.setCustomMaximize(true);
stage.setTitle("Weather API");
stage.setScene(new Scene(decorator));
String cssURI = getClass().getResource("stylesheet/style.css").toExternalForm();
decorator.getStylesheets().add(cssURI);
stage.show();
}
And this is the effect, look at the split-pane.
Without JFXDecorator works normally with code like below.
#Override
public void start(Stage stage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("ApplicationView.fxml"));
Parent root = loader.load();
stage.setTitle("Weather API");
stage.setScene(new Scene(root));
String cssURI = getClass().getResource("stylesheet/style.css").toExternalForm();
stage.show();
}
I tried to set AncorPane properties of main pane similar to childs so maybe it will resising but it failed. My FXML looks like that
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="480.0" prefWidth="640.0" stylesheets="#stylesheet/style.css" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.161">
<children>
<SplitPane dividerPositions="0.29797979797979796" layoutX="220.0" layoutY="129.0" prefHeight="480.0" prefWidth="640.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0" />
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0" />
</items>
</SplitPane>
</children>
</AnchorPane>
How can I fix it? Thank You.
Get rid of the AnchorPane and make the SplitPane the root node.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.SplitPane?>
<?import javafx.scene.layout.AnchorPane?>
<SplitPane dividerPositions="0.29797979797979796" prefHeight="480.0" prefWidth="640.0" xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1">
<items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0" />
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0" />
</items>
</SplitPane>

Java FXML calling methods on button clicks

I am creating a basic game launcher in JavaFX with SceneBuilder. Since SceneBuilder works in FXML, my launcher layout is in FXML. I have a method in my main class that I want to call on a button click. I read that you could use
#methodName
In the button's
onAction
property, but this does not work.
My main Java class:
#FXML
private void launchGame(ActionEvent e) {
System.out.println("Launching...");
}
#Override
public void start(Stage primaryStage) throws IOException {
Parent root = FXMLLoader.load(Main.class.getResource("launcher.fxml"));
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.setTitle("First Week Login");
primaryStage.setResizable(false);
primaryStage.sizeToScene();
primaryStage.show();
}
My FXML file:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.text.Text?>
<?import javafx.scene.web.WebView?>
<AnchorPane xmlns:fx="http://javafx.com/fxml/1"
xmlns="http://javafx.com/javafx/8.0.102">
<children>
<BorderPane prefHeight="493.0" prefWidth="664.0" styleClass="background"
stylesheets="#launcher.css">
<bottom>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0"
BorderPane.alignment="CENTER">
<children>
<Button alignment="CENTER" mnemonicParsing="false"
text="Launch Game" onAction="#launchGame" />
</children>
</HBox>
</bottom>
<top>
<HBox alignment="CENTER" prefHeight="100.0" prefWidth="200.0"
BorderPane.alignment="CENTER">
<children>
<Text strokeType="OUTSIDE" strokeWidth="0.0"
styleClass="title" text="First Week" />
</children>
</HBox>
</top>
<center>
<WebView prefHeight="200.0" prefWidth="200.0"
BorderPane.alignment="CENTER" />
</center>
</BorderPane>
</children>
</AnchorPane>
You need to create a separate Controller class and specify it in top AnchorPane tag with fx:controller="packageName.Classname"
Like this:
<AnchorPane xmlns:fx="http://javafx.com/fxml/1"
xmlns="http://javafx.com/javafx/8.0.102"
fx:controller="com.packageName.Controller">
The called method should be inside the specified Controller class.
com.packageName is just an example, you should use the name of the package where you put the Controller class or no package name if it's not in any package.

SplitPane Divider Position changes on its own

I have a SplitPane that is behaving strangely. It is "spontaneously" moving without interaction from the user.
Here is the sample FXML layout:
<AnchorPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller">
<children>
<SplitPane fx:id="splitPane" dividerPositions="0.29797979797979796" layoutX="100.0" layoutY="67.0" prefHeight="160.0" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0" />
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0" />
</items>
</SplitPane>
</children>
</AnchorPane>
And the controller:
public class Controller {
#FXML
private SplitPane splitPane;
#FXML
private void initialize() {
// Add Listener to the divider value
splitPane.getDividers().get(0).positionProperty().addListener((observable, oldValue, newValue) -> {
System.out.println("newValue = " + newValue);
});
splitPane.getDividers().get(0).setPosition(0.80);
}
}
Here I set the listener on the only divider to output it's current value when it changes. Just below, I set it to 0.80.
Here is the output:
newValue = 0.8
newValue = 0.7986577181208053
You see it does change after initially setting it. While this change is minimal in the example, the problem is exacerbated in my main application.
I am trying to save the divider's position between runs of my program, but with the position value changing on its own every time, the minor issue becomes a major one.
What is happening here and how can I prevent it from changing? Per the FXML, you can see there are no other controls that should be forcing a change due to minimum width, etc.

Make Button inside TextField in FXML

I made a TextField in FXML. I want to add Button in this TextField like clear button. How can I implement this?
You could use an AnchorPane to wrap the TextField and the Button and set the anchors to have the TextField to fill the entire AnchorPane and the Button to be anchored to the right-side.
Example:
<AnchorPane prefHeight="24.0" prefWidth="322.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<TextField prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
<Button graphicTextGap="0.0" minHeight="10.0" minWidth="13.0" mnemonicParsing="false" prefHeight="20.0" prefWidth="22.0" style="-fx-background-color: #7085FF;
-fx-background-radius: 30;
-fx-background-insets: 0;" text="x" textFill="WHITE" AnchorPane.bottomAnchor="4.0" AnchorPane.rightAnchor="4.0" AnchorPane.topAnchor="4.0">
<font>
<Font size="9.0" />
</font>
</Button>
</children>
</AnchorPane>
The output is like:
If you need it more than once (and most probably you will), it is a good idea to have it separately in another FXML file with the corresponding controller class that also handles the action of the Button.
Example with separated FXML:
ButtonedTextField.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.*?>
<?import javafx.scene.text.Font?>
<AnchorPane xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="application.ButtonedTextField">
<children>
<TextField fx:id="textField" prefWidth="200.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
<Button fx:id="button" graphicTextGap="0.0" minHeight="10.0" minWidth="13.0" mnemonicParsing="false" prefHeight="20.0" prefWidth="22.0" style="-fx-background-color: #7085FF;
-fx-background-radius: 30;
-fx-background-insets: 0;" text="x" textFill="WHITE" AnchorPane.bottomAnchor="4.0" AnchorPane.rightAnchor="4.0" AnchorPane.topAnchor="4.0">
<font>
<Font size="9.0" />
</font>
</Button>
</children>
</AnchorPane>
ButtonedTextField.java (controller)
public class ButtonedTextField implements Initializable {
public #FXML TextField textField;
private #FXML Button button;
#Override
public void initialize(URL location, ResourceBundle resources) {
button.setOnAction(e -> textField.setText(""));
}
}
And then you can include this control to another FXML:
ButtonedTextFieldTest.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.layout.*?>
<TabPane xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="application.ButtonedTextFieldTest">
<tabs>
<Tab text="Untitled Tab">
<content>
<AnchorPane id="Content" minHeight="0.0" minWidth="0.0" prefHeight="180.0" prefWidth="200.0">
<children>
<fx:include fx:id="buttonedTextField" source="ButtonedTextField.fxml" prefWidth="200.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="2.0" />
</children>
</AnchorPane>
</content>
</Tab>
</tabs>
</TabPane>
ButtonedTextFieldTest.java (controller of including FXML)
public class ButtonedTextFieldTest implements Initializable {
private #FXML AnchorPane buttonedTextField;
private #FXML ButtonedTextField buttonedTextFieldController;
#Override
public void initialize(URL location, ResourceBundle resources) {
buttonedTextFieldController.textField.textProperty().addListener((ov, oldVal, newVal) -> {
System.out.println(newVal);
});
}
}
Notes:
You can customize the Button as you wish (maybe it is the easier to set the graphic)
The placement and the styling can be further customized.
As you can see in the last class, the control itself and its controller can be also injected! The convention is to have the controller injected is to use the "Controller" postfix after the ID of the inected control.
I have used the CustomTextField class from the ControlsFX project for this kind of functionality before. ControlsFX lacks good documentation, but the CustomTextField class is quite easy to use:
CustomTextField textfield = new CustomTextField
textfield.setRight(new Button());

JavaFX - How can i add a container in an anchor pane

I have a simple project that has a fxml with a splitter.
So the fxml is this:
<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8" fx:controller="accordionproject.FXMLDocumentController">
<children>
<SplitPane fx:id="splitPane" dividerPositions="0.29797979797979796" focusTraversable="true" layoutX="60.0" layoutY="14.0" prefHeight="200.0" prefWidth="320.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" xmlns:fx="http://javafx.com/fxml">
<items>
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0" />
<AnchorPane minHeight="0.0" minWidth="0.0" prefHeight="160.0" prefWidth="100.0" />
</items>
</SplitPane>
</children>
</AnchorPane>
What i would want is to insert a vbox in the left anchor pane from the splitter using only java code.
Can this be done?
I am new to fxml so any help would be apreciated.
Thank you in advance.
Add an fx:id to the AnchorPane you want to manipulate:
<AnchorPane fx:id="leftAnchorPane" minHeight="0.0" minWidth="0.0"
prefHeight="160.0" prefWidth="100.0" />
Get it in your controller as a #FXML member field:
public class FXMLDocumentController
{
#FXML private AnchorPane leftAnchorPane;
...
}
And manipulate it in the desired place (initialize() shown here, can be -almost- anywhere else):
public void initialize() {
VBox vbox = new VBox();
...
AnchorPane.setTopAnchor(vbox, 10.0); // obviously provide your own constraints
leftAnchorPane.getChildren().add(vbox);
}

Categories

Resources