CustomTextField doesn't work when added in Fxml - java

I have TextField with cross to clear input. When I'm adding it like this:
public class Controller implements Initializable {
#FXML
private TextField autoCompleteTextField = TextFields.createClearableTextField();
...
and then:
#Override
public void initialize(URL location, ResourceBundle resources){
autoComplete1 = TextFields.bindAutoCompletion(autoCompleteTextField,callback, converter);
autoCompletePane.getChildren().add(autoCompleteTextField);
Everything works great. But when i want to add this item in SceneBuilder or manualy in Fxml:
<Pane fx:id="autoCompletePane" prefHeight="30.0" prefWidth="444.0">
<children>
<CustomTextField fx:id="autoCompleteTextField" layoutX="25.0" layoutY="3.0" />
// or TextField instead of CustomTextField
</children>
</Pane>
normal TextField is inserted, without cross to clear input.
What can i do?

You can specify a static method not taking parameters to create the textfield using the fx:factory attribute:
<Pane fx:id="autoCompletePane" prefHeight="30.0" prefWidth="444.0">
<children>
<TextFields fx:id="autoCompleteTextField" fx:factory="createClearableTextField" layoutX="25.0" layoutY="3.0" />
</children>
</Pane>
(This requires an import processing instruction for the TextFields class of course.)

Related

JavaFX : remove a component from the component itself

I have a simple JavaFX application with main controller. In this main controller I dynamically add this component :
Added component:
FXML code of this component (paramValue.fxml) :
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.image.Image?>
<?import javafx.scene.image.ImageView?>
<?import javafx.scene.layout.HBox?>
<HBox alignment="CENTER" prefHeight="40.0" prefWidth="833.0" xmlns="http://javafx.com/javafx/8.0.171" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.example.demoSpreadsSheet.ParamValueController">
<children>
<Label prefHeight="95.0" prefWidth="83.0" text="Paramètre : " />
<TextField fx:id="paramName" prefHeight="32.0" prefWidth="300.0" />
<Label prefHeight="95.0" prefWidth="83.0" text="Valeur : ">
<HBox.margin>
<Insets left="40.0" />
</HBox.margin>
</Label>
<TextField fx:id="paramValue" prefHeight="32.0" prefWidth="300.0" />
<Button mnemonicParsing="false" prefHeight="25.0" prefWidth="12.0">
<HBox.margin>
<Insets left="20.0" />
</HBox.margin>
<graphic>
<ImageView fitHeight="26.0" fitWidth="21.0" onMouseClicked="#openSpreadSheet" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="#../../Images/spreadsheet.png" />
</image>
</ImageView>
</graphic>
</Button>
<Button mnemonicParsing="false" onAction="#removeParamValue" prefHeight="25.0" prefWidth="12.0">
<graphic>
<ImageView fitHeight="26.0" fitWidth="21.0" onMouseClicked="#openSpreadSheet" pickOnBounds="true" preserveRatio="true">
<image>
<Image url="#../../Images/remove.png" />
</image>
</ImageView>
</graphic>
<HBox.margin>
<Insets left="10.0" />
</HBox.margin>
</Button>
</children>
</HBox>
Controller of the component :
package com.example.demoSpreadsSheet;
import javafx.fxml.FXML;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
public class ParamValueController extends HBox {
#FXML TextField paramName, paramValue;
public ParamValueController() {}
#FXML
public void openSpreadSheet(){
}
#FXML
private void removeParamValue(){
//I don't know if it's possible to remove it here
}
}
The controller where I add my custom component :
package com.example.demoSpreadsSheet;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import java.io.IOException;
public class DoublonController extends BorderPane {
#FXML VBox paramValueList;
public DoublonController(){}
#FXML
public void initialize() throws IOException {
paramValueList.getChildren().addAll(createParamValueComponent(), createParamValueComponent());
}
private HBox createParamValueComponent() throws IOException {
FXMLLoader paramValueLoader = new FXMLLoader(DoublonController.class.getResource("paramValue.fxml"));
return paramValueLoader.load();
}
#FXML
private void addParamValue() throws IOException {
this.paramValueList.getChildren().add(createParamValueComponent());
}
}
I want to remove this component when the user click on the red cross. The problem is that the redcross calls the function "removeParamValue()" within the class that I want to remove which i think is impossible..
Do you have a workaround ?
Thanks in advance.
There are at least two approaches you can use.
Get the Parent
As mentioned by #kleopatra in a question comment, you can get the parent of the root FXML node from within the "child" controller, check if it is an instance of Pane and, if it is, then modify the parent's children list.
For example:
#FXML
private void removeParamValue() {
var parent = container.getParent();
if (parent instanceof Pane pane) {
pane.getChildren().remove(container);
}
}
This requires you to inject the root FXML element into the controller. I made the name of the field container, whose type would be HBox, but you can name it anything you'd like (just make sure add the corresponding fx:id attribute to the FXML file). I also made use of pattern matching for instanceof, which was finalized in Java 16. If you are not using at least Java 16, then you'll need to manually cast parent to a Pane.
Problem With Your Code
Note you can't use this.getParent(), because the controller itself has never been added to the scene graph. In fact, with how your code is currently written, your controller class should not be extending HBox at all (and I assume a similar problem with your other controller class). Perhaps you meant to use fx:root, but that would involve changing both the root element of your FXML file and how you load the FXML file. Read the linked documentation for more information.
In other words, unless you want to use fx:root, remove the extends HBox and extends BorderPane from your controller classes.
Use a "Callback"
The other approach is to use a callback. Add a method to your ParamValueController which accepts some functional interface (e.g., Runnable):
public void setOnRemove(Runnable action) {
// also declare a field to store the Runnable in
this.action = action;
}
Then your "remove param value" method would look like:
#FXML
private void removeParamValue() {
if (action != null) {
action.run();
}
}
And you'd modify the code that loads the "child" FXML like so:
private HBox createParamValueComponent() throws IOException {
FXMLLoader paramValueLoader = new FXMLLoader(DoublonController.class.getResource("paramValue.fxml"));
HBox root = paramValueLoader.load();
// invoked *after* you load the FXML file
ParamValueController controller = paramValueLoader.getController();
controller.setOnRemove(() -> paramValueList.getChildren().remove(root));
return root;
}
Personally, I prefer this approach as it's safer, more flexible, and in my opinion clearer. But it is also more work than the first approach.

Getting access to ui elements in different fxml file

I have a FXML-application with a main.fxml, which includes two other fxml files. Each of these fxml files has its own controller class.
My question is, of how to get access to objects from a specific controller, although these objects are defined on another fxml file.
The following code is just a minimal example. I thought it was a good idea to split ui elements in different fxml files, because they are getting larger and larger.
My main fxml:
<VBox xmlns:fx="http://javafx.com/fxml/1" fx:controller="MainController">
<fx:include fx:id="top" source="top.fxml"/>
<fx:include fx:id="bottom" source="bottom.fxml"/>
</VBox>
top.fxml:
<VBox fx:id="vbox" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="ControllerTop">
<children>
<Button fx:id="topbtn" onAction="#printOutput" text="OK" />
</children>
</VBox>
bottom.fxml
<VBox fx:id="vbox" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="ControllerBottom">
<children>
<Button fx:id="bottombtn" onAction="#printOutput" text="OK" />
</children>
</VBox>
For top.fxml i have created this controller class:
public class ControllerTop {
#FXML public Button topbtn;
#FXML public Button bottombtn;
#FXML
public void printOutput() {
System.out.println("Hello from top button");
topbtn.setDisable(true); //OK!
bottombtn.setDisable(false); //Failed
}
}
Of course bottombtn is defined in bottom.fxml and has its own controller. The problem is, that bottombtn of printOut() of this ControllerTop results in a NullPointerException. So i need help by accessing objects in a nice and smart way.
Thanks
in main controller:
public class MainController {
/**
* var name has to be topController
*/
public TopController topController;
/**
* var name has to be bottomController
*/
public BottomController bottomController;
public void initialize(){
Button topbtn=topController.topbtn;
Button bottombtn=bottomController.bottombtn;
topbtn.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
System.out.println("Hello from top button");
topbtn.setDisable(true); //OK!
bottombtn.setDisable(false); //Failed
}
});
}
}
bottom.fxml:
<VBox fx:id="vbox" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="BottomController">
<children>
<Button fx:id="bottombtn" text="OK" />
</children>
</VBox>
top.fxml:
<VBox fx:id="vbox" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="TopController">
<children>
<Button fx:id="topbtn" text="OK" />
</children>
</VBox>
and in class TopController and BottomController set #FXML public Button **btnName**;
BottomController:
public class BottomController {
public Button bottombtn;
}
TopController:
public class TopController {
public Button topbtn;
}
Another option it to use initialize at MainController to set the value of bottombtn in topController

How do I make my VBox and Label resize according to texts in label in JavaFx?

As you can see, my texts are constrained by labels. How do I make the label resize to exactly fit the text and also to resize the VBox and GridPane accordingly. I only want to resize vertically.
My FXML:
<VBox fx:id="body"
alignment="TOP_CENTER"
prefHeight="150"
GridPane.vgrow="SOMETIMES"
prefWidth="300"
GridPane.columnIndex="0"
GridPane.rowIndex="1" spacing="5">
<Label alignment="CENTER" textAlignment="JUSTIFY" fx:id="word" maxWidth="268"/>
<Pane prefHeight="10" VBox.vgrow="NEVER"/>
<Label alignment="CENTER" textAlignment="JUSTIFY" wrapText="true" fx:id="meaning" maxWidth="268" />
<Label alignment="CENTER" textAlignment="JUSTIFY" wrapText="true" fx:id="sentence" maxWidth="268" />
</VBox>
This VBox is inside the GridPane:
<GridPane fx:id="base"
alignment="CENTER"
prefHeight="210.0"
maxWidth="310.0"
stylesheets="#style.css"
vgap="0" xmlns="http://javafx.com/javafx/8"
xmlns:fx="http://javafx.com/fxml/1"
fx:controller="sample.Controller">
...
Minimal, Complete, and Verifiable example as requested by #James_D
Main Class:
public class Main extends Application {
private Stage stage;
private StackPane stackPane;
#Override
public void start(Stage primaryStage) throws Exception{
stage = primaryStage;
FXMLLoader loader = new FXMLLoader(getClass().getResource("sample.fxml"));
Parent root = (Parent) loader.load();
stackPane = new StackPane(root);
Scene scene = new Scene(stackPane);
scene.setFill(Color.WHITE);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Controller Class:
public class Controller implements Initializable{
#FXML private Label label1;
#FXML private Button changeLabel;
private StringProperty labelString = new SimpleStringProperty();
#Override
public void initialize(URL location, ResourceBundle resources) {
labelString.setValue("aasdasd sad asdasd asdasda");
label1.textProperty().bind(labelString);
}
#FXML
public void clicked(MouseEvent e){
labelString.setValue("asdsadasd asdasdasd sfdgsfwoef fgtrhfgbdrgdf dfgdfivbjkfd gdfgidfjvdf gdfgjldkvbdf gjdilgjdfv dfgojdflkgdf ");
}
}
sample.fxml:
<GridPane fx:controller="sample.Controller"
xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10" maxHeight="Infinity">
<children>
<VBox fx:id="body"
alignment="CENTER"
maxHeight="Infinity"
GridPane.vgrow="SOMETIMES"
prefWidth="300"
GridPane.columnIndex="0"
GridPane.rowIndex="1" spacing="5">
<Label alignment="CENTER" VBox.vgrow="ALWAYS" wrapText="true" textAlignment="CENTER" fx:id="label1" maxWidth="268"/>
<Button fx:id="changeLabel" text="Change" minWidth="50" onMouseClicked="#clicked" maxWidth="Infinity" />
</VBox>
</children>
Expectation:
When I press 'Change' button, Everything should resize to just give enough space for text to be shown.
Problem:
When I press 'Change' button, UI remains same not showing the full text.
Assuming you want the text to wrap, the labels will need to be able to grow vertically. Add the attribute
maxHeight="Infinity"
to each label. You are also constraining the overall height of the VBox with prefHeight="150"; remove that attribute and let the VBox figure out its own height. Similarly, you probably want to remove the prefHeight attribute from the GridPane.

JavaFX custom component usage

I have a problem with my custom component in JavaFX.
My custom component contains split pane that contains TableView and TabPane:
MasterDetail.fxml
<fx:root type="javafx.scene.layout.VBox" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
<children>
<SplitPane dividerPositions="0.5" orientation="VERTICAL">
<items>
<AnchorPane>
<children>
<TableView fx:id="table"/>
</children>
</AnchorPane>
<AnchorPane>
<children>
<TabPane fx:id="tabPane"/>
</children>
</AnchorPane>
</items>
</SplitPane>
</children>
</fx:root>
I want to use my custom control in another fxml so I created it and how I can declare columns for TableView in my MasterDetail component?
For example I want to do something like this:
<MasterDetail fx:id="masterDetail">
<table>
<columns>
<TableColumn fx:id="nameColumn" prefWidth="75.0" text="Name"/>
<TableColumn fx:id="createDateColumn" prefWidth="75.0" text="Create date"/>
</columns>
</table>
</MasterDetail>
It is ever possible to do this in fxml?
It is possible to define the number of table columns in the 'masterDetail' component. But to happen we have to define custom properties which reads the table columns.
Example :
<CustomeTable>
<children>
<TableView fx:id="table"/>
</children>
</CustomeTable>
Define CustomeTable which extends AnchorPane.
public class CustomeTable extends AnchorPane{
private ObjectProperty<Integer> noOfColumns = new SimpleObjectProperty<Integer>();
public void setNoOfColumns(Integer noOfColumns){
noOfColumns.set(noOfColumns);
}
public Integer getNoOfColumns(){
return noOfColumns.get();
}
public CustomeTable(){
// add the listener to the ObjectProperty
// which updates the noOfColumns into table
noOfCoulmns.addListener();
}
}

ActionHandler error in FXML JAVAFX

I have made a basic login screen in netbeans using javafx and scenebuilder 2.0! by copying a youtube tutorial.
<Button id="btnLogin" layoutX="146.0" layoutY="243.0" mnemonicParsing="false" onAction="#initialize" prefHeight="59.0" prefWidth="76.0" text="Login" />
as shown above, netbeans underline "#initialize" and give the folowing error "Handler method is not accessible. Make public, or annotate with #FXML"
and the program doesn't want to start up or even show me my stage.
LoginController:
#FXML private void initialize (ActionEvent event)
{
System.out.println("test");
}
if i simply delete the code "onAction="#initialize" my program runs and shows me an interface, but my button to login doesn't work(obviously)
P.S. making it public doesnt work
any advice will appreciated thanks a lot in advance
code:
Main:
public class Login extends Application
{
#Override
public void start(Stage stage) throws Exception
{
Parent root = FXMLLoader.load(getClass().getResource("/fxml/Login.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.setTitle("Login");
stage.show();
}
Controller:
public class LoginController implements Initializable {
#FXML
private Label lblmessage;
#FXML
private TextField txtUsername;
#FXML
private Button btnLogin;
#FXML
private PasswordField txtPassword;
#FXML private void handlebutton()
{
System.out.println("test");
}
/**
* Initializes the controller class.
* #param url
* #param rb
*/
#Override
public void initialize(URL url, ResourceBundle rb) {
// TODO
}
}
I guess the method name is the problem.
The method #FMXL protected void initialize() may be used as an additional callback on startup.
You can use the initialize method e.g. to dynamically fill your UI.
So:
Try to rename the method in code and fxml file.
As given here:
An instance of the FXMLLoader class simply looks for the initialize()
method on the controller and calls it, if available. Note that, similar to other FXML callback methods such as event handlers, this method must be annotated with the #FXML annotation if it is not public.
I have just rebuilt your code. I also added this main method to the Login:
public static void main(String[] args){
launch(args);
}
This is the Login.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="LoginController">
<children>
<Label fx:id="lblmessage" text="Label" />
<TextField fx:id="txtUsername" />
<Button fx:id="btnLogin" mnemonicParsing="false" onAction="#handlebutton" text="Button" />
<PasswordField fx:id="txtPassword" />
</children>
</VBox>
And with this setup, everything works as expected.
The Stage is loaded, the handlebutton method is called when I click the button and the initialize method is called on startup.
The error currently seems to be not reproducable.
thanks all! my issue was that in my fxml file i was naming my textfields/password fields fields
<TextField id="txtUsername"....... etc
<Label id="lblmessage" ...... etc
<PasswordField id="txtPassword".... etc
therefore i received a null pointed everytime a null pointer everytime i tried to access those variable from my controller/main class, because my program wasnt recieving any data because i forgot my fX:id, so the above should have looked like the following
<TextField fx:id="txtUsername"....... etc
<Label fx:id="lblmessage" ...... etc
<PasswordField fx:id="txtPassword".... etc
then my program worked perfectly
thanks everyone for their help

Categories

Resources