Absolute layout- centering of buttons - java

I'm working on a project, and I need to make two buttons absolutly centered on the scene.
I haven't found any built in pane option to do the work, so I had to use alot of binding.
The code for that is really long and I'm quite sure there is an easier way to do the job.
Here is a demo: http://screencast.com/t/pvi5WLko
So what I want to know is, if there's an easy way to do the same thing and preferably with built in panes or something.
I want the buttons to be centered, no matter how the window is resized. Example:

The probably easiest way would be to use a VBox:
public void start(final Stage stage) throws Exception {
final Button button0 = new Button("Start learning");
final Button button1 = new Button("Customize");
final VBox box = new VBox();
box.setFillWidth(true);
box.getChildren().setAll(button0, button1);
box.setAlignment(Pos.CENTER);
stage.setScene(new Scene(box));
stage.setWidth(200);
stage.setHeight(100);
stage.show();
}
Another possible way of doing this is with a GridPane:
public void start(final Stage stage) throws Exception {
final Button button0 = new Button("Start learning");
final Button button1 = new Button("Customize");
final GridPane cPane = new GridPane();
cPane.getChildren().addAll(button0, button1);
GridPane.setConstraints(button0, 0, 0, 1, 1, HPos.CENTER, VPos.CENTER);
GridPane.setConstraints(button1, 0, 1, 1, 1, HPos.CENTER, VPos.CENTER);
final ColumnConstraints columnn0 = new ColumnConstraints();
columnn0.setPercentWidth(100);
cPane.getColumnConstraints().addAll(columnn0);
final RowConstraints row0 = new RowConstraints(1);
row0.setPercentHeight(50);
final RowConstraints row1 = new RowConstraints(1);
row1.setPercentHeight(50);
cPane.getRowConstraints().addAll(row0, row1);
stage.setScene(new Scene(cPane));
stage.setWidth(200);
stage.setHeight(100);
stage.show();
}
The idea here would be to configure the rows and columns in the grid to fill your scene using the according constraints objects. The above defines one column and two rows. You can then align your components within the cells of the Grid, using GridPane.setConstraints(...).
You might want to alter the code slightly to have the top button align to VPos.BOTTOM and the lower one to VPos.TOP, depending on whether you want the buttons to stick together (you'll then have to define a margin for both, of course).

Related

JavaFX -- Creating multiple panes within a scene

So I have to make a Zodiac Sign GUI, and we are tasked with having the following:
a Label in the top left, and a TextField in the top right (both with padding)
an exit Button in the center of the GUI, along with a clear and find my sign on either side
and finally, a Label in the bottom center prompting the sign
I am utterly confused on how to have this come out, as I am a novice in JavaFX. I believe I would need a branch node along with the root node in order to get this kind of layout. I do not need assistance in instantiating the button, labels etc., mainly confused with how this layout can even work. The code I have now is the following:
public class ZodiacGUI extends Application {
public static void main(String args[]) {
Application.launch(args);
}
#Override
public void start(Stage primaryStage) throws Exception {
BorderPane mainPane = new BorderPane();
mainPane.setStyle("-fx-background-color: PINK");
setupControls(mainPane);
Scene scene = new Scene(mainPane);
setStage(primaryStage, scene);
}
public void setStage(Stage primaryStage, Scene scene) {
primaryStage.setWidth(500);
primaryStage.setHeight(200);
primaryStage.setTitle("What is my Zodiac Sign?");
primaryStage.setScene(scene);
primaryStage.show();
}
public void setupControls(BorderPane mainPane) {
Label label = new Label("Enter you birthday formatted as -> mm/dd");
Button exitButton = new Button();
Button findSign = new Button();
Button clear = new Button();
TextField userInput = new TextField();
userInput.setPromptText("Enter birthday");
exitButton.setText("Exit.");
findSign.setText("Find my sign.");
clear.setText("Clear.");
exitButton.setOnAction(e -> System.exit(0));
mainPane.setLeft(label);
mainPane.setRight(userInput);
mainPane.setCenter(exitButton);
mainPane.setCenter(findSign);
mainPane.setCenter(clear);
BorderPane.setAlignment(label, Pos.TOP_LEFT);
BorderPane.setAlignment(userInput, Pos.TOP_RIGHT);
BorderPane.setAlignment(exitButton, Pos.CENTER);
BorderPane.setAlignment(findSign, Pos.CENTER_LEFT);
BorderPane.setAlignment(clear, Pos.CENTER_RIGHT);
}
}
This only outputs one of the buttons out of the three, as I assume it is because the necessary addition of another BorderPane? Here is a drawn out picture of what I would like to come out with:
Just to clarify, I do not need assistance with the handling of finding the zodiac sign, etc. Mainly need assistance on the layout, as it has stumped me for days. Thank you in advance for helping out a novice to JavaFX :).
You have three rows with diffrent number of children. You can use HBox if row have more than one child.
BorderPane mainPane = new BorderPane();
mainPane.setTop(new HBox(topLabel, topField));
mainPane.setCenter(new HBox(centerLabel, centerField, centerButtom));
mainPane.setBottom(bottomCenterButton);
If you need more than 3 rows (top, center, bottom section of BorderPane) you can use VBox, where every child is row like:
HBox row1 new HBox(child1, child2)
HBox row2 new HBox(child1, child2, child3)
HBox row3 new HBox(child1)
HBox row4 new HBox(child1, child2)
VBox pane = new VBox(row1, row2, row3, row4);
You might want to use a GridPane
GridPane grid = new GridPane();
grid.add(label,0,0);
grid.add(userInput,2,0);
grid.add(findSign,0,1);
grid.add(exitButton,1,1);
grid.add(clear,2,1);
or use a VBox with BorderPane to help with the layout/alignment
BorderPane mainPane, centerPane;
mainPane.setLeft(label);
mainPane.setRight(userInput);
centerPane.setLeft(findSign);
centerPane.setRight(clear);
centerPane.setCenter(exitButton);
BorderPane.setAlignment(label, Pos.LEFT);
BorderPane.setAlignment(userInput, Pos.RIGHT);
BorderPane.setAlignment(exitButton, Pos.CENTER);
BorderPane.setAlignment(findSign, Pos.LEFT);
BorderPane.setAlignment(clear, Pos.RIGHT);
VBox items = new VBox();
items.getChildren().addAll(mainPane, centerPane);

JavaFX ScrollPane setVvalue() not working properly

I am testing the JavaFX ScrollPane class and realized that it is not working as I expect, I don't know why. I have the following code:
public class Client3 extends Application {
int indexMsg = 0;
Button send;
GridPane root;
ScrollPane msgPane;
GridPane msgPaneContent;
FlowPane writePane;
TextField writeMsg;
Scene scene;
#Override
public void start(Stage primaryStage) {
root = new GridPane();
root.setAlignment(Pos.CENTER);
root.setVgap(10);
root.setPadding(new Insets(10, 10, 10, 10));
msgPane = new ScrollPane();
msgPane.setPrefSize(280, 280);
msgPane.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER);
msgPaneContent = new GridPane();
msgPaneContent.setPrefWidth(270);
msgPaneContent.setVgap(10);
writePane = new FlowPane(10, 10);
writePane.setAlignment(Pos.CENTER);
writePane.setPrefWidth(280);
writeMsg = new TextField();
writeMsg.setPrefWidth(150);
writeMsg.setPromptText("Write your message");
writePane.getChildren().add(writeMsg);
GridPane.setConstraints(msgPane, 0, 0);
GridPane.setConstraints(writePane, 0, 1);
msgPane.setContent(msgPaneContent);
root.getChildren().addAll(msgPane, writePane);
writeMsg.setOnAction((ev) -> {
if (!writeMsg.getText().isEmpty()) {
TextArea msg = new TextArea(writeMsg.getText());
msg.setMaxWidth(135);
msg.setPrefRowCount(msg.getLength() / 21 + 1);
msg.setWrapText(true);
GridPane.setConstraints(msg, 0, indexMsg);
indexMsg++;
writeMsg.deleteText(0, writeMsg.getText().length());
msgPaneContent.getChildren().add(msg);
msgPane.setVvalue(1.0);
}
});
scene = new Scene(root, 300, 300);
primaryStage.setTitle("Chat App");
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Basically, I have a GridPane as the root with a ScrollPane and a GridPane as its children. The ScrollPane has a children GridPane. There is a TextField with an EventHandler which generates a TextArea inside the GridPane (the ScrollPane's children). Each TextArea object is created in the vertical direction, downwards. I want to set the scrollbar always at its maximum value (setVvalue(1.0)) each time a new TextArea is added. The thing is that it doesn't seem to work as it should because the vertical value is never set to the maximum after handling the event, but it seems to be set to the maximum value that it had before handling it (the bottom of the previous TextArea added).
Any solution for this? Thanks in advance.

JavaFx Fill WidthColumn Constraint is not working inside a layout

I’m going to add two text fields in a row of Java FX GridPane layout. And I want to fill one text field throughout the available space. If I add GridPane directly to the Scene, then it is working fine. But If I add GridPane to another layout like Group (or any other layout) then fill property is not working correctly.
Following code is working as expected.
public void start(final Stage stage) {
GridPane parent = new GridPane();
TextField addEmail = new TextField();
TextField addFirstName = new TextField();
parent.add(addEmail, 0, 0);
parent.add(addFirstName, 1, 0);
ColumnConstraints cons1 = new ColumnConstraints();
cons1.setFillWidth(true);
cons1.setHgrow(Priority.ALWAYS);
parent.getColumnConstraints().add(cons1);
Scene scene = new Scene(parent, 400, 300, Color.WHITE);
stage.setScene(scene);
stage.show();
}
Following one is not working (But I want to make this work).
public void start(final Stage stage) {
GridPane parent = new GridPane();
TextField addEmail = new TextField();
TextField addFirstName = new TextField();
parent.add(addEmail, 0, 0);
parent.add(addFirstName, 1, 0);
ColumnConstraints cons1 = new ColumnConstraints();
cons1.setFillWidth(true);
cons1.setHgrow(Priority.ALWAYS);
parent.getColumnConstraints().add(cons1);
Group group = new Group();
group.getChildren().addAll(parent);
Scene scene = new Scene(group, 400, 300, Color.WHITE);
stage.setScene(scene);
stage.show();
}
I’m new to Java FX hence my approach may completely wrong please direct me to the correct path.
Sorry, but you've chosen the wrong parent.
From the javadoc of Group:
By default, a Group will "auto-size" its managed resizable children to their preferred sizes during the layout pass to ensure that Regions and Controls are sized properly as their state changes.
A Group simply does not modify the size of it's children other than resizing them to the preferred size.
Only if you put the GridPane in a parent that resizes it, like e.g. StackPane, it's size is modified...

JavaFX: Trouble binding properties to panes

I am a beginner who just recently started learning JavaFX, and I seem to be making the same reoccurring mistake within my programs. For example, in the following code I am trying to draw the X-Axis and Y-Axis and have it binded to half the width and height of the pane. When executed, the axes are very small and located at the topleft corner, but as you resize the window of the application, the axes slowly increase in size until the window not being resized anymore.
public class Debug extends Application {
#Override
public void start(Stage primaryStage) {
Pane pane = new Pane();
GraphFunc test = new GraphFunc();
pane.getChildren().add(test);
Scene scene = new Scene(pane, 500, 500);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
class GraphFunc extends Pane {
private double xAxisSpan, yAxisSpan;
public GraphFunc() {
xAxisSpan = 100;
yAxisSpan = 100;
drawAxes();
}
private void drawAxes() {
Line xAxis = new Line(0, 50, 100, 50);
Line yAxis = new Line(50, 0, 50, 100);
xAxis.setStartX(0);
xAxis.startYProperty().bind(heightProperty().divide(2));
xAxis.endXProperty().bind(widthProperty());
xAxis.endYProperty().bind(heightProperty().divide(2));
yAxis.startXProperty().bind(widthProperty().divide(2));
yAxis.setStartY(0);
yAxis.endXProperty().bind(widthProperty().divide(2));
yAxis.endYProperty().bind(heightProperty());
getChildren().addAll(xAxis, yAxis);
}
}
I am confused because when pane is change to a StackPane, this does not happen. Also if I moved the code in drawAxes() to start() and added the lines to pane it would also not do this. Please explain, I cannot seem to understand what is happening after researching and playing around with it.
This is happening because of Pane. Pane is meant to be used when absolute positioning of children is required.
This is from Oracle documentation:
This class may be used directly in cases where absolute positioning of children is required since it does not perform layout beyond resizing resizable children to their preferred sizes. It is the application's responsibility to position the children since the pane leaves the positions alone during layout.
Pane pane = new Pane();
GraphFunc test = new GraphFunc();
pane.getChildren().add(test);
Scene scene = new Scene(pane, 500, 500);
If you remove the width and height from the scene and instead set the width and height directly on to test like this:
public void start(Stage primaryStage) {
GridPane pane = new GridPane();
GraphFunc test = new GraphFunc();
test.setPrefSize(500, 500);
pane.getChildren().add(test);
Scene scene = new Scene(pane);
primaryStage.setScene(scene);
primaryStage.show();
}
You will get this:
drawn image
However you will not be able to span.
From Oracle documentation:
Note: if an application needs children to be kept aligned within a
parent (centered, positioned at top-left, etc), it should use a
StackPane instead.
Also from Oracle documentation:
Pane does not clip its content by default, so it is possible that
childrens' bounds may extend outside its own bounds, either if
children are positioned at negative coordinates or the pane is resized
smaller than its preferred size.

Resize menu into layer

I have this JavaFX menu which is not working properly:
public class CreatingMenus {
// Generate Menu
public void initMenu(Stage primaryStage, Group root, Scene scene) {
// Call Menu Actions from Java Method MenuActions
final MenuActions ma = new MenuActions();
MenuBar menuBar = new MenuBar();
// File menu - new, save, close, exit
Menu menu = new Menu("File");
menu.getItems().add(new MenuItem("New"));
menu.getItems().add(new MenuItem("Save"));
menu.getItems().add(new MenuItem("Close"));
menu.getItems().add(new SeparatorMenuItem());
MenuItem menuItem = new MenuItem("Exit");
// Exit from the application
menuItem.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
// This Java Method is called from the Java Class MenuActions
ma.programExit(); // Exit program
}
});
menu.getItems().add(menuItem);
menuBar.getMenus().add(menu);
// Options menu - Preferences
Menu options = new Menu("Options");
options.getItems().add(new MenuItem("Preferences"));
menuBar.getMenus().add(options);
// Help menu - About
Menu help = new Menu("Help");
MenuItem helpItem = new MenuItem("About");
// Exit from the application
helpItem.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent e) {
aboutDialog();
}
});
help.getItems().add(helpItem);
menuBar.getMenus().add(help);
menuBar.prefWidthProperty().bind(primaryStage.widthProperty());
menuBar.setLayoutX(0);
menuBar.setLayoutY(0);
root.getChildren().add(menuBar);
}
public void aboutDialog() {
final int xSize = 640;
final int ySize = 480;
final String logoImage = "/logo.png";
final Color backgroundColor = Color.WHITE;
final String text = "SQL Browser";
final String version = "Product Version: 1.0";
final String license = "License Information";
final Stage aboutDialog = new Stage();
aboutDialog.initModality(Modality.WINDOW_MODAL);
GridPane grid = new GridPane();
grid.setAlignment(Pos.CENTER);
grid.setHgap(10);
grid.setVgap(10);
grid.setPadding(new Insets(20, 20, 20, 20));
// Logo
Image image = new Image(getClass().getResourceAsStream(logoImage));
ImageView logo = new ImageView(image);
grid.add(logo, 1, 0);
// Product name
Text productName = new Text(text);
productName.setFont(Font.font("Verdana", 12));
grid.add(productName, 0, 2);
// Product version
Text productVersion = new Text(version);
productVersion.setFont(Font.font("Verdana", 12));
grid.add(productVersion, 0, 3);
// Product License
Text productLicense = new Text(license);
productLicense.setFont(Font.font("Verdana", 12));
grid.add(productLicense, 0, 4);
// Close Button
Button closeButton = new Button("Close");
closeButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
// Close the dialog when "Close" button is pressed
aboutDialog.close();
}
});
grid.add(closeButton, 5, 18);
// Configure dialog size and background color
Scene aboutDialogScene = new Scene(grid, xSize, ySize, backgroundColor);
aboutDialog.setScene(aboutDialogScene);
aboutDialog.show();
}
}
When I add this menu it overrides the other components into the application and they are not responsible because I suppose they are behind this layer. Can you tell how I can isolate this menu in separate layer because I don't want to place other components behind.
EDIT
When I use the menu code I get this result:
When I remove the the menu code I get this result and the application is working properly:
The problem somewhere into the menu code.
Scene Width Binding
Stage width contains window borders and a title bar - which are irrelevant to internal layout.
To fill the scene width with something using binding, bind the preferred size of the thing to a scene size and not the stage size:
menuBar.prefWidthProperty().bind(menuBar.getScene().widthProperty());
Or (usually preferred) you can use alternate layout panes and layout constraints which perform the sizing automatically. For your particular layout a BorderPane might be useful.
You may have additional issues with your implementation which cause problems (I have not checked).
Using Groups for Layout
Your pastebin code uses a Group for the main scene layout root. Group is generally a poor choice for laying out a flexibly sized screen like your example. A group is not re-sizable. Instead it is advisable to use a re-sizable layout pane as referred to above.
Tool Support
ScenicView is invaluable in debugging existing layout issues.
SceneBuilder is great for rapid prototyping of various layouts.
I strongly advise using these tools to help understand how layouts work and to provide you with the tools and knowledge you need to debug layout issues yourself.
Works for me without any changes. You might should check your Java/JavaFx versions. I am using 1.7_17, 64 bits on windows.
I have added some images (I don't have yours) to get sure the behaviour you get is not because of them.

Categories