So i’m recreating the structure of a calculator on javafx. Not for the functionality, but only for the looks. I have created the buttons and set the gaps between them to be 16, horizontally and vertically. However, when I had a text field object to display the numbers/results (again not for functionality, but for display), the buttons are affected by the size of the text field object. Therefore, the first row where I placed the text field object has a huge gap.
This is the code I have for that section:
// layout manager: organize window contents
GridPane root = new GridPane();
// Scene: contains window content
// parameters: layout manager; width window; height window
Scene mainScene = new Scene(root, 600, 600);
// attach/display Scene on Stage (window)
mainStage.setScene( mainScene );
// create TextField
TextField nameField = new TextField("");
root.add(nameField, 3, 1);
root.setStyle( "-fx-font-size: 24;" );
// to add space between rows and columns
root.setHgap( 16 ); // between horizontal objects
root.setVgap( 16 ); // between vertical objects
// create all buttons
Button button0 = new Button("0");
Button button1 = new Button("1");
Button button2 = new Button("2");
Button button3 = new Button("3");
Button button4 = new Button("4");
...
// parameters: object, column # (X), row # (Y)
// first row
root.add(buttonAllClear, 3, 2);
root.add(buttonClear, 4, 2);
root.add(buttonDelete, 5, 2);
root.add(buttonDivides, 6, 2);
// second row
root.add(button7, 3, 3);
root.add(button8, 4, 3);
root.add(button9, 5, 3);
root.add(buttonTimes, 6, 3);
//
//when I run the code i get:
//[ text field ]
//ac c del /
//7 8 9 *
//4 5 6 -
//1 2 3 +
//0 . =
//but i want:
//[ text field ]
//ac c del /
//7 8 9 *
//4 5 6 -
//1 2 3 +
//0 . =
3
Spanning multiple columns with a TextField
You can set the columnSpan
on a node in GridPane
to have it span a defined number of columns.
GridPane.setColumnSpan(
textField,
4
);
The span can also be configured while adding a node (as pointed at by Trashgod in comments).
gridPane.add(textField, 0, 0, 4, 1);
Separating the layout into different panes
Alternately, you could not place the text field for the calculator in the grid, instead have a VBox
containing the textfield and gridPane. Then only use the GridPane
for the buttons.
Example
This example demonstrates both approaches, using columnSpan
for the equals button and placing the text field in a separate row in a VBox
.
import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextField;
import javafx.scene.layout.ColumnConstraints;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import java.util.Arrays;
public class CalcApp extends Application {
@Override
public void start(Stage stage) {
String[][] buttonText = {
{"ac", "c", "del", "/"},
{ "7", "8", "9", "*"},
{ "4", "5", "6", "-"},
{ "1", "2", "3", "+"},
{ "1", "2", "="}
};
GridPane buttonGrid = new GridPane(5, 5);
for (int r = 0; r < buttonText.length; r++) {
String[] rowText = buttonText[r];
Button[] buttonsInRow =
Arrays.stream(rowText)
.map(CalcApp::createButton)
.toArray(Button[]::new);
buttonGrid.addRow(r, buttonsInRow);
}
buttonGrid.getChildren().stream()
.filter(CalcApp::isEqualsButton)
.forEach(equalsButton ->
GridPane.setColumnSpan(
equalsButton,
2
)
);
int nCols = buttonText[0].length;
for (int c = 0; c < nCols; c++) {
ColumnConstraints columnConstraints = new ColumnConstraints();
columnConstraints.setPercentWidth(100.0 / nCols);
buttonGrid.getColumnConstraints().add(columnConstraints);
}
TextField textField = new TextField();
textField.setStyle("-fx-font-size: 20px;");
textField.setMinHeight(TextField.USE_PREF_SIZE);
VBox layout = new VBox(10, textField, buttonGrid);
layout.setPadding(new Insets(10));
VBox.setVgrow(buttonGrid, Priority.SOMETIMES);
stage.setScene(new Scene(layout));
stage.show();
}
private static boolean isEqualsButton(Node n) {
return n instanceof Button b && "=".equals(b.getText());
}
private static Button createButton(String text) {
Button button = new Button(text);
button.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
button.setMinSize(Button.USE_PREF_SIZE, Button.USE_PREF_SIZE);
GridPane.setVgrow(button, Priority.ALWAYS);
GridPane.setHgrow(button, Priority.ALWAYS);
button.setStyle("-fx-font-size: 20px;");
return button;
}
public static void main(String[] args) {
launch();
}
}
Alternative: using a TilePane
A similar solution that uses a TilePane
rather than GridPane
, but keeps the TextField
out of the TilePane
which contains the buttons.
1