I created this JavaFX dialog with Close button:
final int xSize = 300;
final int ySize = 280;
final Color backgroundColor = Color.WHITE;
final String text = "SQL Browser Version 1.0";
final Stage aboutDialog = new Stage();
aboutDialog.initModality(Modality.WINDOW_MODAL);
Button closeButton = new Button("Close");
closeButton.setAlignment(Pos.BOTTOM_CENTER);
closeButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
aboutDialog.close();
}
});
Scene aboutDialogScene = new Scene(VBoxBuilder.create()
.children(new Text(text), closeButton)
.alignment(Pos.CENTER)
.padding(new Insets(10))
.build(), xSize, ySize, backgroundColor);
aboutDialog.setScene(aboutDialogScene);
aboutDialog.show();
I want to display the button at the bottom of the dialog. I used this to set the alignment:
closeButton.setAlignment(Pos.BOTTOM_CENTER); but for some reason the button is displayed at the center of the dialog. Can you tell me how I can fix this?
If you want to use a VBox for this, the method you are looking for is:
VBox.setVgrow(node, Priority.ALWAYS);
By default a VBox will just place children one under the other from the top left of where you place it. The children don't expand to fill all of the available vertical area, unless you set a Vgrow constraint on a child with an unbounded max height.
A few different ways you can get the layout you seek (there are others as well):
Use a StackPane instead of a VBox and align your button with StackPane.setAlignment(closeButton, Pos.BOTTOM_CENTER);
Use an AnchorPane instead of a VBox and set constraints on the AnchorPane appropriately.
Use a spring region which is an empty Region which expands to fill empty space.
Sample spring region:
Region topSpring = new Region();
Region bottomSpring = new Region();
Scene aboutDialogScene = new Scene(VBoxBuilder.create()
.children(topSpring, new Text(text), bottomSpring, closeButton)
.alignment(Pos.CENTER)
.padding(new Insets(10))
.build(), xSize, ySize, backgroundColor);
VBox.setVgrow(topSpring, Priority.ALWAYS);
VBox.setVgrow(bottomSpring, Priority.ALWAYS);
Calling closeButton.setAlignment(Pos.BOTTOM_CENTER); sets the alignment of things (text and graphic) within the closeButton, not the alignment of the closeButton within it's parent (which is what you really want).
For understanding how layout constraints work, SceneBuilder is a good tool to play around with and ScenicView can help debug layout issues in existing code.
Here are a few FXML samples of your layout that you can load up into SceneBuilder to see how the different layout options work.
All of the samples below can easily be written in plain java using the JavaFX API if you prefer. I wrote them in fxml as it makes the layouts easy to preview in SceneBuilder.
FXML sample using a StackPane:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<StackPane id="StackPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="280.0" prefWidth="300.0" xmlns:fx="http://javafx.com/fxml">
<children>
<Label text="SQL Browser Version 1.0" />
<Button mnemonicParsing="false" text="Button" StackPane.alignment="BOTTOM_CENTER" />
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</StackPane>
And the same thing with some spring regions:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<VBox alignment="CENTER" prefHeight="280.0" prefWidth="300.0" xmlns:fx="http://javafx.com/fxml">
<children>
<Region prefHeight="-1.0" prefWidth="-1.0" VBox.vgrow="ALWAYS" />
<Label text="SQL Browser Version 1.0" />
<Region prefHeight="-1.0" prefWidth="-1.0" VBox.vgrow="ALWAYS" />
<Button mnemonicParsing="false" text="Close" />
</children>
</VBox>
And the same thing with the label itself set to expand to fill empty space in the VBox:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<VBox alignment="CENTER" prefHeight="280.0" prefWidth="300.0" xmlns:fx="http://javafx.com/fxml">
<children>
<Label maxHeight="1.7976931348623157E308" text="SQL Browser Version 1.0" VBox.vgrow="ALWAYS" />
<Button mnemonicParsing="false" text="Close" />
</children>
<padding>
<Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
</padding>
</VBox>
Related
I'm using Scene Builder (v11.0.0) to create FXML files for scenes in JavaFX (v12) but, despite instructing all containers to USE_COMPUTED_SIZE for the preferred widths and heights, the rendered scenes (as seen in Scene Builder and also when run as a JavaFX application which loads those FXML files) are being clipped at the right and bottom edges so that bits of nodes are chopped off.
And in Scene Builder it seems that the renderer must know that the scene won't fit the allowed bounds because the editor shows blue boundary markers which are clearly some way beyond the rendered rectangle.
View in Scene Builder
The view in Scene Builder shows that more space is needed at the bottom in order to give the buttons sufficient space (their bottom edge, and the lower edge of the TitledPane is missing). And more space is needed at the right in order to fit the right edges of the DatePicker and TitledPane. The blue boundary markers show clearly where the actual content ends, so it's not clear why the display area is being calculated to be several pixels shorter than this.
View of running Java application
Once the FXML files are used to populate a window in a JavaFX application, the same thing is seen: the calculated size for the window is a number of pixels too few to fit the whole scene correctly.
If the blue boundary markers have correctly been calculated to show that extra display area width and height are needed, how do I tell the FXML to require this additional space when rendering?
Is this a known bug/limitation in Scene Builder, FXML, or JavaFX. Or is there something more I need to do beyond just selecting USE_COMPUTED_SIZE for the preferred dimensions?
In order to make this explicit, see the example FXML below which displays the problem illustrated.
scene.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TitledPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
<children>
<TitledPane animated="false" collapsible="false" text="untitled">
<content>
<HBox>
<children>
<fx:include source="subscene.fxml" />
</children>
</HBox>
</content>
</TitledPane>
<TitledPane animated="false" collapsible="false" text="untitled">
<content>
<HBox>
<children>
<fx:include source="subscene.fxml" />
</children>
</HBox>
</content>
</TitledPane>
<TitledPane animated="false" collapsible="false" text="untitled">
<content>
<HBox alignment="BASELINE_RIGHT">
<children>
<Button mnemonicParsing="false" text="Button" />
<Button mnemonicParsing="false" text="Button" />
</children>
</HBox>
</content>
</TitledPane>
</children>
</VBox>
subscene.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.DatePicker?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.VBox?>
<VBox maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
<children>
<Label text="Label" />
<DatePicker />
</children>
</VBox>
This does appear to be a bug in JavaFX, specifically DatePicker, as this simple example can reproduce the problem:
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.DatePicker;
import javafx.scene.layout.Region;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage primaryStage) {
VBox root = new VBox(new DatePicker());
// Problem shows up when using USE_COMPUTED_SIZE (the default) as well
root.setMinSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
root.setMaxSize(Region.USE_PREF_SIZE, Region.USE_PREF_SIZE);
primaryStage.setScene(new Scene(root));
primaryStage.show();
}
}
Resulting in:
Note: It does not seem to matter what parent the DatePicker is put in. Nor does the problem appear with other controls.
A workaround to this issue appears to be calling Window.sizeToScene() after calling show(). I don't understand why that would make a difference, but it does. Unfortunately, this will only help in the real application, not in Scene Builder.
I have one container in an FXML file (let's call this the main container w/ main controller) and am trying to generate other FXML-created nodes and add them as children in the main container.
I don't think I can do this by nesting the FXML since the nodes are being added to a custom component that requires function calling. I thought I could load the nodes in the main controller's initialize but this leads to a stack overflow since the loading calls the initialize function recursively.
What's the best way to do this? I want to be able to have the main controller respond to and setup the nodes. I could
Main controller looks like:
public class MainController implements Initializable {
#FXML
private CardPane cardPane;
#Override
public void initialize(URL url, ResourceBundle rb) {
// setup panes
AnchorPane importPane = new AnchorPane();
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("ImportPane.fxml"));
fxmlLoader.setRoot(importPane);
fxmlLoader.setController(this);
try {
fxmlLoader.load();
cardPane.addCard(importPane);
} catch (IOException exception) {
throw new RuntimeException(exception);
}
}
CardPane is the custom component class where addCard adds a node (an implementation of CardLayout).
The FXML for ImportPane is pretty much empty right now. I just have a CSS styling for the root class so I can see how it's laying out.
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.net.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<fx:root id="AnchorPane" prefHeight="400.0" prefWidth="600.0" styleClass="mainFxmlClass" type="AnchorPane" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.40">
<stylesheets>
<URL value="#view.css" />
</stylesheets>
<children>
<VBox alignment="CENTER" layoutX="121.0" layoutY="38.0" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" styleClass="importPane" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
</children>
</fx:root>
The FXML for the main component is:
<?xml version="1.0" encoding="UTF-8"?>
<?import cardPane.*?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane id="AnchorPane" prefHeight="200" prefWidth="320" stylesheets="#PHASIQAnalyzerView.css" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="phasiqplateanalyzer.PHASIQAnalyzerController">
<children>
<VBox alignment="CENTER" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
<children>
<HBox id="buttonBar" alignment="CENTER" maxHeight="-Infinity" maxWidth="1.7976931348623157E308" minWidth="-Infinity" prefHeight="100.0" prefWidth="200.0" />
<CardPane id="mainPane" fx:id="cardPane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" VBox.vgrow="ALWAYS" />
</children>
</VBox>
</children>
</AnchorPane>
remove fx:controller="phasiqplateanalyzer.PHASIQAnalyzerController"from the FXML and use the setController() method of an FXMLLoader object (no static method call for that...)
https://docs.oracle.com/javase/8/javafx/api/javafx/fxml/FXMLLoader.html#setController-java.lang.Object-
FXMLLoader fxmlLoader =new FXMLLoader();
fxmlLoader.setController(yourControllerClass);
fxmlLoader.load("yourFxmlFile");
I am developing a simple app using javafx. I experience one problem. I want to populate my HBox layout with some data, and want to do this in my Controller class. I do not know how to refernce a method inside my Controller class to the element in my FXML file, so that as soon as my app starts, and main window loads, the HBox gets populated with data. Which attribute of HBox element should I use?
Here is some code:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<Pane prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="gui.Controller" >
<children>
<HBox layoutX="386.0" layoutY="14.0" prefHeight="371.0" prefWidth="200.0" fx:id="hbox" />
</children>
</Pane>
Just modify the ObservableList returned by Pane.getChildren().
StackPane layoutY="70.0" prefHeight="479.0". I want to make the values (70.0) and (479.0) static in a Java file so I can use them for other files.
Is this possible?
If your constant is defined in a class:
public class SomeClass {
public static final double DEFAULT_HEIGHT = 479 ;
// ...
}
then you can access it in FXML as follows:
<StackPane>
<prefHeight>
<SomeClass fx:constant="DEFAULT_HEIGHT" />
</prefHeight>
</StackPane>
Make sure you have the appropriate import in the fxml file for the class you are using.
James_D showed you the way of doing it with a custom class. Another way of doing it in fxml is to define your own variables. But they are not shareable across files.
Instead of this
<StackPane layoutY="70.0" prefHeight="479.0">
You want to have
<StackPane layoutY="$variable" prefHeight="$variable">
You be able to do it like this
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane xmlns:fx="http://javafx.com/fxml/1" id="AnchorPane" prefHeight="200" prefWidth="320" fx:controller="javafxapplication22.FXMLDocumentController">
<fx:define>
<Double fx:id="layoutY" fx:value="70.0"/>
<Double fx:id="prefHeight" fx:value="479.0"/>
</fx:define>
<children>
<StackPane layoutY="$layoutY" prefHeight="$prefHeight"/>
<Pane layoutY="$layoutY" prefHeight="$prefHeight"/>
</children>
</AnchorPane>
This question already has answers here:
How to implement a NumberField in javaFX 2.0?
(8 answers)
Closed 9 years ago.
I'm new to Java and JavaFX, but I'm trying to build a pretty basic program. Below is a snippet of code I had originally placed in the On Key Pressed event of the TextField (it did of course have a different parameter type). However, the code didn't work there (i.e. the user was still allowed to enter characters), and after some Googling I found that maybe this code should be moved to the On Input Method Text Changed event of the TextField. But, I'm unable to determine how I can recover the key that was pressed and whether or not CTRL is down.
private void verifyKeyIsInteger(InputMethodEvent event) {
KeyCode code = event.getCode();
if (event.isControlDown() && (code.equals(KeyCode.C) || code.equals(KeyCode.X) || code.equals(KeyCode.V))) {
return;
}
else if (code.isDigitKey()) {
return;
}
event.consume();
}
This code is in my controller that is attached to the FXML file.
How can I ensure that user's can only input integers into the TextField?
EDIT
Here is my current FXML:
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>
<AnchorPane id="AnchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml" fx:controller="tutoringcalculator.MainFormController">
<children>
<Label layoutX="14.0" layoutY="14.0" text="Session Minutes:" />
<TextField fx:id="sessionMinutes" layoutX="100.0" layoutY="14.0" prefWidth="200.0" />
<Label layoutX="14.0" layoutY="34.0" text="Earnings:" />
<TextField fx:id="earnings" layoutX="100.0" layoutY="34.0" prefWidth="200.0" />
<Button fx:id="quitButton" layoutX="511.0" layoutY="14.0" minWidth="75.0" mnemonicParsing="false" onMouseClicked="#quitApplication" text="Quit" />
</children>
</AnchorPane>
If you need to track integer input, you can have a look at the implementation of IntegerField of JavaFX (a field which is used to access only integer values) :
IntegerField.java
IntegerFieldSkin.java
But this approach is not applicable, if CTRL is needed to be tracked.