JavaFX create and use custom control with it's own functions - java

I have created a simple custom control as shown in picture:
when I click on "Click me" button the console shows me this message "The button was clicked!" by a function called doSomething, and this is the custom control's fxml file code:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<fx:root prefHeight="83.0" prefWidth="196.0" type="javafx.scene.layout.AnchorPane" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
<TextField fx:id="textField" layoutY="2.0" prefHeight="25.0" prefWidth="196.0" />
<Button layoutX="72.0" layoutY="29.0" mnemonicParsing="false" onAction="#doSomething" text="Click me" />
</children>
</fx:root>
And this is the controller of this custom control:
package control;
import java.io.IOException;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
public class Controller extends AnchorPane {
#FXML private TextField textField;
public Controller() {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("CustomControlView.fxml"));
fxmlLoader.setRoot(this);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
public String getText() {
return textProperty().get();
}
public void setText(String value) {
textProperty().set(value);
}
public StringProperty textProperty() {
return textField.textProperty();
}
#FXML
protected void doSomething() {
System.out.println("The button was clicked!");
}
}
The control is working good and I can call it in another fxml file using "JavaFX scence builder" (as shown in picture) :
And this is the fxml file code:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="237.0" prefWidth="324.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
<javafx.scene.layout.AnchorPane layoutX="64.0" layoutY="92.0" prefHeight="40.0" prefWidth="196.0">
<children>
<TextField fx:id="textField" layoutY="2.0" prefHeight="25.0" prefWidth="196.0" />
<Button layoutX="72.0" layoutY="29.0" mnemonicParsing="false" onAction="#doSomething" text="Click me" />
</children>
</javafx.scene.layout.AnchorPane>
<Label layoutX="75.0" layoutY="14.0" text="Trying the custom control:">
<font>
<Font size="15.0" />
</font>
</Label>
</children>
</AnchorPane>
But the problem is that I have to redefine the doSomething function of the custom control button !!!I mean when I added my custom conrol to another fxml file all the functions of this custom control should work without redefining it just like Swing.
Am I wrong ?

The problem is that the Scene Builder just copies the code of your custom control into the new control rather than linking it. To use your custom control you have to use the fx:include FXML tag.
So the FXML file should look something like:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.text.*?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane prefHeight="237.0" prefWidth="324.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
<fx:include source="yourCustomControl.fxml" />
<Label layoutX="75.0" layoutY="14.0" text="Trying the custom control:">
<font>
<Font size="15.0" />
</font>
</Label>
</children>
</AnchorPane>
However, you have to do this manually. As far as I know, the Scene Builder is not capable of doing it.

Related

Unreadable characters when I try to create a new project

It is my first time to use IntelliJ and JavaFX, so I followed this guide to practice. But I ended up with some unreadable characters and I don't know how to solve it.
I'm supposed to get this.
What I got
fxml file:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.control.Button?>
<VBox alignment="CENTER" spacing="20.0" xmlns:fx="http://javafx.com/fxml"
fx:controller="com.example.javafx.HelloController">
<padding>
<Insets bottom="20.0" left="20.0" right="20.0" top="20.0"/>
</padding>
<Label fx:id="welcomeText"/>
<Button text="Hello!" onAction="#onHelloButtonClick"/>
</VBox>
HelloController.java
package com.example.javafx;
import javafx.fxml.FXML;
import javafx.scene.control.Label;
public class HelloController {
#FXML
private Label welcomeText;
#FXML
protected void onHelloButtonClick() {
welcomeText.setText("Welcome to JavaFX Application!");
}
}

SceneBuilder cannot create instance of custom component with given set of properties

I created this custom component:
public class IconButton extends Button {
#FXML private ImageView imageView;
private IconButtonState state;
private String fullIconUrl;
private String outlineIconUrl;
public IconButton(#NamedArg("fullIconUrl") String fullIconUrl,
#NamedArg("outlineIconUrl") String outlineIconUrl) {
URL url = getClass().getResource(View.ICON_BUTTON.getFileName());
FXMLLoader loader = new FXMLLoader(url);
loader.setRoot(this);
loader.setController(this);
state = IconButtonState.NOT_INITIALIZED;
this.fullIconUrl = fullIconUrl;
this.outlineIconUrl = outlineIconUrl;
try {
loader.load();
} catch (IOException exception) {
exception.printStackTrace();
throw new RuntimeException(exception);
}
}
#FXML
public void initialize() {
this.state = IconButtonState.ACTIVE;
String url = buildUrl(fullIconUrl);
Image image = new Image(url);
imageView.setImage(image);
}
}
<!-- icon-button.fxml -->
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.*?>
<fx:root mnemonicParsing="false" prefHeight="65.0" prefWidth="98.0" style="-fx-background-color: transparent;" type="Button" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1">
<graphic>
<ImageView fx:id="imageView" fitHeight="64.0" fitWidth="48.0" pickOnBounds="true" preserveRatio="true">
</ImageView>
</graphic>
</fx:root>
Then, I instantiated my IconButton component in another fxml file like this:
<!-- home.fxml -->
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.layout.*?>
<?import agill.deshopp.components.*?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="768.0" prefWidth="1024.0" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1">
<top>
<HBox styleClass="app-bar" stylesheets="#../css/home.css">
<padding>
<Insets bottom="15.0" top="15.0" />
</padding>
<children>
<IconButton fullIconUrl="form-full" outlineIconUrl="form-outline"></IconButton>
<IconButton fullIconUrl="message-full" outlineIconUrl="message-outline"></IconButton>
<IconButton fullIconUrl="chart-full" outlineIconUrl="chart-outline"></IconButton>
<Region HBox.hgrow="ALWAYS"></Region>
<IconButton fullIconUrl="settings-full" outlineIconUrl="settings-outline"></IconButton>
</children>
</HBox>
</top>
<center>
<Pane BorderPane.alignment="CENTER" />
</center>
</BorderPane>
The code runs fine and the screen renders as expected. However, I can't open the file in SceneBuilder. It prompts me with this exception:
java.lang.RuntimeException: Cannot create instance of agill.deshopp.components.IconButton with given set of properties: [fullIconUrl, outlineIconUrl]
javafx.fxml.LoadException:
/home/allan/IdeaProjects/california/src/main/resources/agill/deshopp/fxml/home.fxml:14
How do I fix this?
The code posted in the question is not mre and can not be invoked.
However the following code is an mre. It runs with no exceptions.
Fxml files can be edited using ScreenBuilder.
Modify it to your needs to find out what's wrong in the code posted in the question.
Note that form-full throws exception so I used form_full and that the fxml assignment should include $ sign: fullIconUrl="$form_full".
package fx_tests.test;
import java.io.IOException;
import java.net.URL;
import javafx.beans.NamedArg;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.control.Button;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
public class IconButton extends Button {
#FXML private ImageView imageView;
private final String fullIconUrl;
public IconButton(#NamedArg("fullIconUrl") String fullIconUrl) {
this.fullIconUrl = fullIconUrl;
URL url = getClass().getResource("icon-button.fxml");
FXMLLoader loader = new FXMLLoader(url);
loader.setRoot(this);
loader.setController(this);
try {
loader.load();
} catch (IOException exception) {
exception.printStackTrace();
}
}
#FXML
public void initialize() {
Image image = new Image(fullIconUrl);
imageView.setImage(image);
}
}
Home.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.*?>
<?import javafx.scene.layout.*?>
<?import fx_tests.test.*?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="168.0"
prefWidth="124.0" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1">
<top>
<HBox>
<padding>
<Insets bottom="15.0" top="15.0" />
</padding>
<children>
<IconButton fullIconUrl="$form_full"></IconButton>
</children>
</HBox>
</top>
<center>
<Pane BorderPane.alignment="CENTER" />
</center>
</BorderPane>
icon-button.fxml:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import javafx.scene.image.*?>
<fx:root mnemonicParsing="false" prefHeight="65.0" prefWidth="98.0" style="-fx-background-color: transparent;"
type="Button" xmlns="http://javafx.com/javafx/10.0.2-internal" xmlns:fx="http://javafx.com/fxml/1">
<graphic>
<ImageView fx:id="imageView" fitHeight="64.0" fitWidth="48.0" pickOnBounds="true" preserveRatio="true">
</ImageView>
</graphic>
</fx:root>
Test with:
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
private static final String IMAGE = "https://www.shareicon.net/data/128x128/2015/03/28/14104_animal_256x256.png";
#Override
public void start(Stage currentStage) throws Exception {
FXMLLoader loader = new FXMLLoader(getClass().getResource("Home.fxml"));
loader.getNamespace().put("form_full", IMAGE);
Parent root=loader.load();
currentStage.setScene(new Scene(root));
currentStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
When you create a custom component in JavaFX, SceneBuilder need to know the definition of this component so it can load it. I have a few custom components of my own and had to export them as a jar, and import them into SceneBuilder. Here is an answer that gives instructions on how to do this:
Adding a custom component to SceneBuilder 2.0
One catch. SceneBuilder only supports up to Java 11. So your custom component must be built and exported to a jar with this version. I had problems with Java 14 and had to backport my specific JavaFX components to Java 11. Good luck!

Root value already specified Exception in CustomController while loading a FXML

first of all: I have tried:
fxml loader exception "root value is already specified" I never specified a root value
javafx exception : Controller value already specified
LoadException: Root value already specified on custom control
But no solution worked for me: I deleted the fx:controller tag from my FXML -> Exception, added fx:root type="BorderPanealways the same Exception:
Caused by: java.lang.RuntimeException: javafx.fxml.LoadException: Root value already specified
When I remove the statement fxmlLoader.setRoot(this) from my custom controller no Exception is thrown but the layout is empty. For better understanding, below are my code snippets from my MainScreenController where I want to add the custom controller as child and my CustomController class ImageContainer
Help is appreciated!
Code snippet MainScreenController (only a snippet):
public class MainScreenController{
public TextField tf_userName;
public ListView lv_listView;
public FlowPane fp_contentFlowPane;
public SplitPane sp_splitPane;
public void onItemClicked(MouseEvent mouseEvent) throws IOException {
int index = lv_listView.getSelectionModel().getSelectedIndex();
if (mouseEvent.getButton().equals(MouseButton.SECONDARY)) {
if (index >= 0) {
lv_listView.getItems().remove(index);
userList.remove(index);
}
}
else{
ImageContainer imgC = new ImageContainer(4,2,"location");
fp_contentFlowPane.getChildren().add(imgC);
}
}}
Code of my Custom controller, ImageContainer:
public class ImageContainer extends Pane {
public HBox hbx_elementContainer;
public Label lb_likeCount;
public Label lb_commentCount;
public Label lb_location;
public Label lb_accountHolder;
public ImageView iv_feedImage;
private String imageLink;
public ImageContainer(int likeCount, int commentCount, String location) {
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("/image_container.fxml"));
fxmlLoader.setController(this);
fxmlLoader.setRoot(this);
try {
fxmlLoader.load();
} catch (IOException e) {
throw new RuntimeException(e);
}
this.lb_likeCount.setText(String.valueOf(likeCount));
this.lb_commentCount.setText(String.valueOf(commentCount));
this.lb_location.setText(location);
Image image = new Image("/sampleFoto.JPG");
iv_feedImage.setImage(image);
}
}
FXML file of the layout I want to set:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.text.Font?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="200.0" prefWidth="200.0" xmlns="http://javafx.com/javafx/8.0.172-ea" xmlns:fx="http://javafx.com/fxml/1">
<bottom>
<HBox fx:id="hbx_elementContainer" prefHeight="31.0" prefWidth="600.0" BorderPane.alignment="CENTER">
<children>
<Label fx:id="lb_likeCount" contentDisplay="TOP" text="Label">
<HBox.margin>
<Insets right="10.0" />
</HBox.margin></Label>
<Label fx:id="lb_commentCount" text="Label">
<HBox.margin>
<Insets right="20.0" />
</HBox.margin></Label>
<Label fx:id="lb_location" text="Label" />
<Label fx:id="lb_accountHolder" text="Label" />
<Button mnemonicParsing="false" text="Download">
<font>
<Font name="Arial Bold" size="11.0" />
</font>
<HBox.margin>
<Insets right="10.0" />
</HBox.margin>
</Button>
</children>
</HBox>
</bottom>
<center>
<AnchorPane prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<children>
<ImageView fx:id="iv_feedImage" fitHeight="150.0" fitWidth="200.0" pickOnBounds="true" preserveRatio="true" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children>
</AnchorPane>
</center>
</BorderPane>

Javafx Path to a specific folder and select the file entered on textfield

I wasn't very clear on my previous question, and I just rephrased it. Here is what I trying to achieve.
I want to define a path to a folder;
on the FROM ->TextField, Customer will enter the image name which already exist in the folder;
Click on a button and the file name will be added to the path and populate it to the imageView without manually browsing to the folder.
So, how can I do that? Thanks
ImageViewerController .java
public class ImageViewerController implements Initializable {
private Label label;
#FXML
private ImageView imageView;
#FXML
private TextField txt_Path;
private Image image;
String name;
#Override
public void initialize(URL url, ResourceBundle rb) {
}
#FXML
private void btn_Valide(ActionEvent event) {
loadImage();
}
private void loadImage(){
name = txt_Path.getText();
File file = new File("src/images/"+name);
Image image = new Image(file.toURI().toString());
imageView.setImage(image);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.image.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="521.0" prefWidth="660.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="imageviewer.ImageViewerController">
<center>
<Pane prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
<children>
<Pane layoutX="24.0" layoutY="248.0" prefHeight="248.0" prefWidth="306.0" style="-fx-background-color: #EBDEF0;">
<children>
<ImageView fx:id="imageView" fitHeight="237.0" fitWidth="297.0" layoutX="7.0" layoutY="8.0" pickOnBounds="true" preserveRatio="true" />
</children>
</Pane>
<TextField fx:id="txt_Path" layoutX="24.0" layoutY="28.0" prefHeight="25.0" prefWidth="510.0" text="avatar.jpg" />
<Button layoutX="549.0" layoutY="28.0" mnemonicParsing="false" onAction="#btn_Valide" prefHeight="25.0" prefWidth="77.0" text="Valide" />
</children>
</Pane>
</center>
</BorderPane>

Buttons do not work in executable JAR

I am working on simple application which I need to make executable.
I am trying to switch from one scene to another in same window. Everything work perfectly when running in Intellij. However, when I created executable app suddenly control buttons stopped working.
There is my main class:
package code;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) throws Exception{
Parent root = FXMLLoader.load(getClass().getResource("FxmlLoginMenu.fxml"));
primaryStage.setTitle("Quiz application");
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Controller Class:
One method inside which is executed when button pressed
package code;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.stage.Stage;
import java.io.IOException;
public class Controller {
#FXML public Button signUp;
#FXML public void signUp() throws IOException {
Parent tableViewParent = FXMLLoader.load(getClass().getResource("FXMLRegister.fxml"));
Scene tableViewScene = new Scene(tableViewParent);
Stage window = (Stage) signUp.getScene().getWindow();
window.setScene(tableViewScene);
window.show();
}
}
And the last FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.MenuBar?>
<?import javafx.scene.control.PasswordField?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.effect.InnerShadow?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.VBox?>
<?import javafx.scene.shape.Line?>
<?import javafx.scene.text.Font?>
<?import javafx.scene.text.Text?>
<AnchorPane fx:controller="code.Controller" prefHeight="385.0" prefWidth="437.0" xmlns="http://javafx.com/javafx/9.0.4" xmlns:fx="http://javafx.com/fxml/1" >
<children >
<MenuBar />
<AnchorPane id="login" maxHeight="-1.0" maxWidth="-1.0" prefHeight="418.0" prefWidth="626.0" style="-fx-background-color: #74AFAD#74AFAD;" VBox.vgrow="ALWAYS">
<children>
<AnchorPane id="login2" layoutX="325.0" layoutY="40.0" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="291.0" prefWidth="313.0" style="-fx-background-color: #D9853B#D9853B;">
<children>
<TextField fx:id="loginField" alignment="TOP_CENTER" layoutX="75.0" layoutY="95.0" promptText="Login">
<effect>
<InnerShadow />
</effect>
</TextField>
<PasswordField fx:id="passwordField" alignment="TOP_CENTER" layoutX="75.0" layoutY="159.0" promptText="Password">
<effect>
<InnerShadow blurType="TWO_PASS_BOX" />
</effect>
</PasswordField>
<Button fx:id="loginButton" layoutX="177.0" layoutY="247.0" mnemonicParsing="false" prefHeight="27.0" prefWidth="65.0" style="-fx-background-color: #74AFAD#74AFAD #74AFAD#74AFAD;" text="Login" />
<Button fx:id="signUp" onAction="#signUp" layoutX="75.0" layoutY="247.0" mnemonicParsing="false" style="-fx-background-color: #74AFAD#74AFAD #74AFAD#74AFAD;" text="Sign up" />
<Button fx:id="signUpAdmin" layoutX="242.0" layoutY="8.0" mnemonicParsing="false" style="-fx-background-color: #74AFAD#74AFAD #74AFAD#74AFAD;" text="Admin" />
</children>
</AnchorPane>
<Line endX="100.0" fill="#ff2323" layoutX="360.0" layoutY="190.0" rotate="90.0" startX="-190.0" stroke="#e43030" strokeLineJoin="ROUND" strokeMiterLimit="0.0" />
<Text fill="#d9853b" layoutX="21.0" layoutY="202.0" strokeType="OUTSIDE" strokeWidth="0.0" text="Welcome to Quiz app" wrappingWidth="284.21875">
<font>
<Font size="27.0" />
</font>
</Text>
</children>
</AnchorPane>
<AnchorPane />
</children>
</AnchorPane>
Ok, I managed to fix that.
The problem was in the Controller class. It tried to load FXML with
FXMLLoader.load(getClass().getResource("FXMLRegister.fxml"));
where it should be
FXMLLoader.load(getClass().getResource("FxmlRegister.fxml"));
Apparently IntelliJ did not catch that error so it was working while running by IntelliJ.

Categories