JavaFX GridPane Object Alignment - java

I am trying to use JavaFX to create a scene with the program's title positioned at the top-center, and buttons in a vertical line along the left side of the scene. However, both of these elements are displayed clustered up in the top-right of the scene, instead of where I want them to be.
How can I get these elements to be displayed where I want them to?
Here is how I try to set the program title's position:
grid.add(gameTitle, 0, 0);
GridPane.setHalignment(gameTitle, HPos.CENTER);
GridPane.setValignment(gameTitle, VPos.TOP);
I try to set the VBox object similarly:
grid.getChildren().add(buttonBox);
GridPane.setHalignment(buttonBox, HPos.LEFT);
GridPane.setValignment(buttonBox, VPos.CENTER);
This is what is displayed:
My entire MainMenu class. (This class is called in my Main class to construct the scene):
package scenes;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.VBox;
import javafx.application.Platform;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.control.Button;
import javafx.scene.layout.Pane;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
public class MainMenu {
public Pane getMainMenuPane() {
// Create the scene grid
GridPane grid = new GridPane();
grid.setHgap(10);
grid.setVgap(10);
// Set the game title to the top center
Text gameTitle = new Text("Bandit King");
Font titleFont = new Font(75);
gameTitle.setFont(titleFont);
//
grid.add(gameTitle, 0, 0);
GridPane.setHalignment(gameTitle, HPos.CENTER);
GridPane.setValignment(gameTitle, VPos.TOP);
// Create Button objects and put in VBox
Button[] buttArr = makeButtons();
VBox buttonBox = new VBox();
buttonBox.getChildren().addAll(buttArr);
buttonBox.setSpacing(10);
// add Button VBox to GridPane
grid.getChildren().add(buttonBox);
GridPane.setHalignment(buttonBox, HPos.LEFT);
GridPane.setValignment(buttonBox, VPos.CENTER);
return (Pane) grid;
}
private Button[] makeButtons() {
// Create buttons
Button start = new Button("Start a New Game");
Button load = new Button("Load a Saved Game");
Button exit = new Button("Exit the Game");
// set Button actions
start.setOnAction( a -> {
System.out.println("WIP- start game.");
});
load.setOnAction( a -> {
System.out.println("WIP- load game");
});
exit.setOnAction( a -> {
Platform.exit();
System.exit(0);
});
// return Button[] array
Button[] buttArr = {start, load, exit};
return buttArr;
}
}
My Main class (Displays the scene):
package central;
import javafx.stage.Stage;
import scenes.*;
import controllers.*;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
public class Main extends Application {
// Get scene panes
private static Pane mainMenu = new MainMenu().getMainMenuPane();
// Create SceneController object.
private static Scene scene = new Scene(mainMenu, 1600, 900);
public static SceneController SceneControl = new SceneController(scene);
#Override
public void start(Stage stage) {
stage.setTitle("Bandit King");
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}

The default cell you add the children of a GridPane to is (0, 0) which is what you do in this line:
grid.getChildren().add(buttonBox);
you need to change this to
grid.add(buttonBox, 0, 1);
to set the row index to 1. (There are alternatives to assigning the row index this way, but this is the most convenient option in this case.)
This won't result in the first column taking the full width of the GridPane though. If you also want the first column to take all the width available, you need to specify this by adding ColumnConstraints:
ColumnConstraints constraints = new ColumnConstraints();
constraints.setHgrow(Priority.ALWAYS);
grid.getColumnConstraints().add(constraints);

As far as what I noticed, you added all the nodes in a column and set there positions, but you did not specify how much the column needs to be stretched. GridPane column will not stretch automatically by itself unless specified.
You can debug your program, by enabling the gridLinesVisible of GridPane property to true.
grid.setGridLinesVisible(true);
You need to specify the columnConstraints, to let the GridPane column stretch to the available width.
ColumnConstraints constraint = new ColumnConstraints();
constraint.setHgrow(Priority.ALWAYS);
grid.getColumnConstraints().add(constraint);

Related

Why are my JavaFX buttons unevenly spaced?

I'm new to JavaFX, trying to build a GUI program that displays a bill for a table at a restaurant when you click on that table. The spacing is off between the table buttons and I'm not sure why.
The GUI class for my program:
package restaurantBillingProgram;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.scene.control.Label;
import javafx.scene.control.Button;
import javafx.geometry.Pos;
public class BillingGUI extends Application {
#Override
public void start(Stage primaryStage) {
// Create grid pane
GridPane pane = new GridPane();
pane.setAlignment(Pos.CENTER);
pane.setHgap(5);
pane.setVgap(5);
// Label
pane.add(new Label("Generate bill"), 1, 0);
// Buttons
Button btT1 = new Button("Table 1");
pane.add(btT1, 0, 1);
btT1.setOnAction(e - > Billing.generateT1());
Button btT2 = new Button("Table 2");
pane.add(btT2, 1, 1);
btT2.setOnAction(e - > Billing.generateT2());
Button btT3 = new Button("Table 3");
pane.add(btT3, 2, 1);
btT3.setOnAction(e - > Billing.generateT3());
// Create scene and place in stage
Scene scene = new Scene(pane, 250, 250);
primaryStage.setTitle("Restaurant Billing Program");
primaryStage.setScene(scene);
primaryStage.show();
}
// Main method
public static void main(String[] args) {
launch(args);
}
}
From the Javadoc:
Row/Column Sizing
By default, rows and columns will be sized to fit their content; a column will be wide enough to accommodate the widest child, ...
The label in row 0 column 1 forces that column to be wider.
You probably want the label to be centered and span all 3 columns.
While doing you layout, use pane.setGridLinesVisible(true). This should only be used during debugging. It can be very useful for situations like your current situation. As #Jim Garrison pointed out, your Label is causing the issue:
Issue:
One way to fix this is to let the Label span all columns and center the Label's text.
Fix:
Key Code:
label.setMaxWidth(Double.MAX_VALUE);
label.setAlignment(Pos.CENTER);
pane.add(label, 0, 0, 3, 1);// Look at the following link to see how this add method works. https://openjfx.io/javadoc/11/javafx.graphics/javafx/scene/layout/GridPane.html#add(javafx.scene.Node,int,int,int,int)
Full Code:
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.layout.GridPane;
import javafx.scene.control.Label;
import javafx.scene.control.Button;
import javafx.geometry.Pos;
public class BillingGUI extends Application {
#Override
public void start(Stage primaryStage) {
// Create grid pane
GridPane pane = new GridPane();
pane.setAlignment(Pos.CENTER);
pane.setHgap(5);
pane.setVgap(5);
pane.setGridLinesVisible(true);//Use for debugging only!!!!
// Label
Label label = new Label("Generate bill");
label.setMaxWidth(Double.MAX_VALUE);
label.setAlignment(Pos.CENTER);
pane.add(label, 0, 0, 3, 1);
// Buttons
Button btT1 = new Button("Table 1");
pane.add(btT1, 0, 1);
Button btT2 = new Button("Table 2");
pane.add(btT2, 1, 1);
Button btT3 = new Button("Table 3");
pane.add(btT3, 2, 1);
// Create scene and place in stage
Scene scene = new Scene(pane, 250, 250);
primaryStage.setTitle("Restaurant Billing Program");
primaryStage.setScene(scene);
primaryStage.show();
}
// Main method
public static void main(String[] args) {
launch(args);
}
}

TabPane is not working on my Application

I have a project from uni where I have to make an application with Java (in a model view controller format), and I want to make tabs in my application, but it doesn't seem to be working.
I looked up a lot of tutorials, and they all tell me the same way of how to use TabPane, but it doesn't work for me.
Here is the code I have in my Application Loader class:
package main;
import controller.ModuleChooserController;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.stage.Stage;
import model.StudentProfile;
import view.ModuleChooserRootPane;
public class ApplicationLoader extends Application {
private ModuleChooserRootPane view;
#Override
public void init() {
//create model and view and pass their references to the controller
StudentProfile model = new StudentProfile();
view = new ModuleChooserRootPane();
new ModuleChooserController(view, model);
}
#Override
public void start(Stage stage) throws Exception {
//whilst you can set a min width and height (example shown below) for the stage window,
//you should not set a max width or height and the application should
//be able to be maximised to fill the screen and ideally behave sensibly when resized
stage.setMinWidth(530);
stage.setMinHeight(500);
TabPane tabPane = new TabPane();
Tab tab = new Tab("Testing");
tabPane.getTabs().add(tab);
stage.setTitle("Final Year Module Chooser Tool");
stage.setScene(new Scene(view));
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
I have the TabPane implemented, but nothing comes up. I also tried implementing the TabPane in my "view" package, but I had no luck there either.
Here is the code for the ModuleRootChooserPane:
package view;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
//You may change this class to extend another type if you wish
public class ModuleChooserRootPane extends BorderPane {
private ButtonPane bp;
private ProfileCreator profileCreator;
public ModuleChooserRootPane() {
//This sets the colour of background
this.setStyle("-fx-background-color: #EDF1F3;");
//Creates a new instance of the buttonPane (Used from ButtonPane.java) and ProfileCreator
bp = new ButtonPane();
profileCreator = new ProfileCreator();
//This adds the padding on the left so that "submit" button is in line with text fields
bp.setPadding(new Insets(0, 0, 0, 120));
//Creates a new VBox which adds the ProfileCreator and the button pane
VBox rootContainer = new VBox(profileCreator, bp);
rootContainer.setPadding(new Insets(100,100,100,100));
this.getChildren().add(rootContainer);
}
}
You choose to work without fxml file, so you need to create your view into the class ModuleChooserRootPane, every graphic element have to be here, or in other classes used here.
So you have to add your TabPane in its constructor too :
public ModuleChooserRootPane() {
...
//this.getChildren().add(rootContainer);
setLeft(rootContainer); // or Top/Bottom/Right/Center
TabPane tabPane = new TabPane();
Tab tab = new Tab("Testing");
tabPane.getTabs().add(tab);
setCenter(tabPane); // or Top/Bottom/Right/Left
}
A BorderPane is good idea for root element because it's has several zones to add element, but fo sor you need to use setLeft(), setRight(), setCenter(), setTop() and setBottom() rather than just getChildren().add() where you cannot control the place
Example for adding content in the different tabs :
TabPane tabPane = new TabPane();
Tab tab = new Tab("Testing");
tab.setContent(new VBox(new Label("Here is the testing place"), new Circle(15, 12, 10)));
Tab tab2 = new Tab("Testing2");
HBox hboxContentTab2 = new HBox();
hboxContentTab2.getChildren().add(new Ellipse(10, 10, 10, 13));
hboxContentTab2.getChildren().add(new Label("Here is the BIS testing place"));
tab2.setContent(hboxContentTab2); // add a Node created before, ot can be whatever you wan, borderpane, gridpane, hbox, vbox, label ...
tabPane.getTabs().addAll(tab, tab2);

Javafx buttons not functioning properly, and pane not resizing

Currently the code below produces a BorderPane with a GridPane in the center and a HBox on the bottom to hold two buttons. The left-most pane in the GridPane contains the text "Name Here". Right now I only want the buttons to move the text "Name Here" up and down but they will not move the text.
I think it has something to do with the particular GridPane node, but I'm not sure. Additionally, I don't know why the left-most GridPane takes up more space relative to the right-most GridPane within the center of the BorderPane.
Any advice will be greatly appreciated, thank you!
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.geometry.Pos;
import javafx.geometry.HPos;
import javafx.geometry.VPos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.Priority;
import javafx.scene.text.Text;
public class differentWindows extends Application {
protected Text name = new Text("Name Here");
protected BorderPane getPane() {
// HBox to hold the up and down buttons
HBox paneForButtons = new HBox(20);
Button btUp = new Button("Up");
Button btDown = new Button("Down");
paneForButtons.getChildren().addAll(btUp, btDown);
paneForButtons.setAlignment(Pos.BOTTOM_LEFT);
// Grid pane to go in center of the border pane, for the name and video
GridPane paneForTextNVideo = new GridPane();
paneForTextNVideo.setAlignment(Pos.CENTER);
paneForTextNVideo.setGridLinesVisible(true);
paneForTextNVideo.add(name, 0, 0);
Text temp = new Text("temp");
paneForTextNVideo.add(temp, 1, 0);
paneForTextNVideo.setHalignment(temp, HPos.CENTER);
paneForTextNVideo.setValignment(temp, VPos.CENTER);
paneForTextNVideo.setHgrow(temp, Priority.ALWAYS);
paneForTextNVideo.setVgrow(temp, Priority.ALWAYS);
paneForTextNVideo.setHalignment(name, HPos.CENTER);
paneForTextNVideo.setValignment(name, VPos.CENTER);
paneForTextNVideo.setHgrow(name, Priority.ALWAYS);
paneForTextNVideo.setVgrow(name, Priority.ALWAYS);
// Border pane to hold all windows
BorderPane pane = new BorderPane();
pane.setBottom(paneForButtons);
pane.setCenter(paneForTextNVideo);
btUp.setOnAction(e -> name.setY(name.getY() - 10));
btDown.setOnAction(e -> name.setY(name.getY() + 10));
return pane;
} // end of the getPane method
#Override
public void start(Stage primaryStage) {
Scene scene = new Scene(getPane(), 450, 200);
primaryStage.setTitle("Assignment #7");
primaryStage.setScene(scene);
primaryStage.show();
} // end of start method
public static void main(String[] args) {
Application.launch(args);
}
} // end of class
Try using setLayoutY instead of setY:
btUp.setOnAction(e -> name.setLayoutY(name.getLayoutY() - 10));
btDown.setOnAction(e -> name.setLayoutY(name.getLayoutY() + 10));
As a sidenote, the Node parent class also has a relocate method for easily changing both the X and Y coordinates:

JavaFX Centering VBox inside GridPane

would really appreciate some help with my problem.
I'm using JavaFX and the NetBeans IDE.
I am making a very simple launch window for a desktop client.
The window currently looks like this image.
Current Look:
Wanted Look (editted with paint):
In other words, I want the buttons to be:
centered underneath the 'Welcome' text
maintain their current width
My current setup is:
I have a GridPane acting as the root. Not sure why JavaFX chose to use (col,row), but that's how I'll represent my setup in the following:
#GridPane(0,0) -> 'Welcome' Text
#GridPane(0,1) -> VBox (this VBox contains the 2 buttons shown in above image)
The relevant code (or what I think is relevant, please correct me if I am wrong) is below:
//adds GridPane to Scene
GridPane grid = new GridPane();
RowConstraints row1 = new RowConstraints();
row1.setPercentHeight(25);
grid.getRowConstraints().add(row1);
grid.setAlignment(Pos.CENTER);
Scene scene = new Scene(grid, 400, 300,Color.WHITESMOKE);
//Create welcome message and add it to grid, and align it to the center
Text sceneTitle = new Text("Welcome");
grid.add(sceneTitle,0,0);
GridPane.setHalignment(sceneTitle,HPos.CENTER);
//Create buttons and fix their widths
Button newBtn = new Button("Create New Project");
Button loadBtn = new Button("Load Existing Project");
newBtn.setMaxWidth(150);
loadBtn.setMaxWidth(150);
//Create a VBox, add children to it, and then add it to the grid
VBox vBtns = new VBox();
vBtns.setSpacing(5);
vBtns.getChildren().addAll(newBtn,loadBtn);
grid.add(vBtns,0,1);
I've tried a lot of different things.. to mention some of the more 'logical' ones:
//1. Center the VBox within its current GridPane cell
GridPane.setHalignment(vBtns,HPos.CENTER);
//2. Center the buttons within the VBox
newBtn.setAlignment(Pos.CENTER);
loadBtn.setAlignment(Pos.CENTER);
I've never been good at GUI programming. It's very discouraging when something that should be so simple takes so long to figure out.
Are there any developers out there who can help me find a solution?
To fix it with your current setup, you just need:
vBtns.setAlignment(Pos.CENTER);
However, this just seems overly complex. Why not just do
import javafx.application.Application;
import javafx.geometry.HPos;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.RowConstraints;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class LayoutTest extends Application {
#Override
public void start(Stage primaryStage) {
GridPane grid = new GridPane();
RowConstraints row1 = new RowConstraints();
row1.setPercentHeight(25);
grid.getRowConstraints().add(row1);
ColumnConstraints colConstraints = new ColumnConstraints();
colConstraints.setHalignment(HPos.CENTER);
grid.getColumnConstraints().add(colConstraints);
grid.setAlignment(Pos.CENTER);
Scene scene = new Scene(grid, 400, 300,Color.WHITESMOKE);
//Create welcome message and add it to grid, and align it to the center
Text sceneTitle = new Text("Welcome");
sceneTitle.setStyle("-fx-font-size:48;");
grid.add(sceneTitle,0,0);
//Create buttons and fix their widths
Button newBtn = new Button("Create New Project");
Button loadBtn = new Button("Load Existing Project");
newBtn.setMaxWidth(150);
loadBtn.setMaxWidth(150);
grid.add(newBtn, 0, 1);
GridPane.setMargin(loadBtn, new Insets(5, 0, 5, 0));
grid.add(loadBtn, 0, 2);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}

JavaFX: Make node take no space, but let its parent decide its position

I have a TextField and a ListView. As the user types in the TextField, suggestions come up in the ListView:
When the TextField is empty, the ListView disappears, by setting the visible and managed properties to false.
However, when the user starts to type, the ListView takes up space and pushes everything down. Using .setManaged(false) allows it not to take up any space, but it doesn't display anymore, as I haven't defined a position for it. I have tried setting the layoutX and layoutY of the search list, but it still doesn't display.
Ideally I'd like the ListView's position to be affected by the layout but not to take up any space.
Any ideas?
Wrap the container that holds the text field(s) in an AnchorPane. Add the ListView to the AnchorPane after the text field container (so it stays on top). Then you need to position the ListView appropriately relative to the text field when you make it visible; I think the best way to do this is to first convert the bounds of the text field from local coordinates to Scene coordinates, then convert those bounds to the coordinates relative to the AnchorPane.
Here's an SSCCE:
import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.geometry.Bounds;
import javafx.geometry.Insets;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
public class SuggestionList extends Application {
#Override
public void start(Stage primaryStage) {
AnchorPane root = new AnchorPane();
ListView<String> suggestionBox = new ListView<>();
suggestionBox.getItems().addAll("Here", "Are", "Some", "Suggestions");
suggestionBox.setMaxHeight(100);
suggestionBox.setVisible(false);
// Grid pane to hold a bunch of text fields:
GridPane form = new GridPane();
for (int i=0; i<10; i++) {
form.addRow(i, new Label("Enter Text:"), createTextField(suggestionBox));
}
// just move the grid pane a little to test suggestion box positioning:
AnchorPane.setLeftAnchor(form, 20.0);
AnchorPane.setRightAnchor(form, 20.0);
AnchorPane.setTopAnchor(form, 20.0);
AnchorPane.setBottomAnchor(form, 20.0);
// allows focus on grid pane, so user can click on it to remove focus from text field.
form.setFocusTraversable(true);
root.setPadding(new Insets(20));
root.getChildren().addAll(form, suggestionBox);
Scene scene = new Scene(root, 600, 400);
primaryStage.setScene(scene);
primaryStage.show();
}
private TextField createTextField(ListView<String> suggestionBox) {
TextField textField = new TextField();
ChangeListener<String> selectionListener = (obs, oldItem, newItem) -> {
if (newItem != null) {
textField.setText(newItem);
}
};
textField.focusedProperty().addListener((obs, wasFocused, isNowFocused) -> {
if (isNowFocused) {
suggestionBox.setVisible(true);
// compute bounds of text field relative to suggestion box's parent:
Parent parent = suggestionBox.getParent(); // (actually the anchor pane)
Bounds tfBounds = textField.getBoundsInLocal();
Bounds tfBoundsInScene = textField.localToScene(tfBounds);
Bounds tfBoundsInParent = parent.sceneToLocal(tfBoundsInScene);
// position suggestion box:
suggestionBox.setLayoutX(tfBoundsInParent.getMinX());
suggestionBox.setLayoutY(tfBoundsInParent.getMaxY());
suggestionBox.setPrefWidth(tfBoundsInParent.getWidth());
suggestionBox.getSelectionModel().selectedItemProperty().addListener(selectionListener);
} else {
suggestionBox.setVisible(false);
suggestionBox.getSelectionModel().selectedItemProperty().removeListener(selectionListener);
}
});
textField.setOnAction(event -> {
suggestionBox.setVisible(false);
suggestionBox.getSelectionModel().selectedItemProperty().removeListener(selectionListener);
});
return textField ;
}
public static void main(String[] args) {
launch(args);
}
}
You might be able to use similar positional tricks and just add it to the same scene, with managed set to false.

Categories

Resources