Reputation: 2987
So I am using a GridPane with the aim of having it's child notes resize to fill the available space provided to the GridPane based on the porportions assigned to the components when they were added to the grid. I have spent time on SO looking at similar questions, but none of the poposed solutions seems to work, or fit my requirements. Basically I want something like this and with dynamic resizing:
I can have that if I size the buttons min, max and pref sizes in either the CSS or code. What I find with this is that the buttons will resize a little bit with the window is resized, but even if I set the pref max of both width and height to a very large value, they still remain as shown above, i.e. they do not take up the whole screen as shown below. It's worth noting that the Magenta color is applied to the GridPane and not the AnchorPane it lives in so it's clearly taking up all the space itself but not allocating it to the children proportionally.
If I take out the CSS specified pref, min and max dimensions and leave only the setMaxSize instructions in the Java code the windows are all drawn small. As shown below.
Below is an SCE. (I know the code can be optimized but it's just meant to be a simple and clear example).
JAVA: package application;
import javafx.application.Application;
import javafx.stage.Stage;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.GridPane;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root,400,400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
ButtonPanel2 bp = new ButtonPanel2();
root.setCenter(bp);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}//end start
public static void main(String[] args) {
launch(args);
}//end main
}//end class main
class ButtonPanel2 extends AnchorPane {
GridPane grid;
Button ba, bb, bc, bd;
/**Construct a new button panel object.**/
public ButtonPanel2(){
//Create Grid and gaps
grid = new GridPane();
grid.setHgap(10);
grid.setVgap(10);
grid.prefWidthProperty().bind(this.widthProperty());
grid.getStyleClass().add("test");
//Init buttons
ba = new Button("A");
bb = new Button("B");
bc = new Button("C");
bd = new Button("D");
//Apply CSS styles for size
ba.getStyleClass().add("button1");
bb.getStyleClass().add("buttonH2");
bc.getStyleClass().add("buttonV2");
bd.getStyleClass().add("button2");
ba.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
bb.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
bc.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
bd.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
//Add items to grid.
//Node, colIndex, rowIndex, colSpan, rowSpan
grid.add(ba,0,0,1,1);//
grid.add(bb,1,0,2,1);//
grid.add(bc,0,1,1,2);//
grid.add(bd,1,1,2,2);//
GridPane.setFillHeight(ba, true);
GridPane.setFillHeight(bb, true);
GridPane.setFillHeight(bc, true);
GridPane.setFillHeight(bd, true);
GridPane.setFillWidth(ba, true);
GridPane.setFillWidth(bb, true);
GridPane.setFillWidth(bc, true);
GridPane.setFillWidth(bd, true);
//anchor grid to parent container (anchor)
AnchorPane.setTopAnchor(grid, 0.0);
AnchorPane.setBottomAnchor(grid, 0.0);
AnchorPane.setLeftAnchor(grid, 0.0);
AnchorPane.setRightAnchor(grid, 0.0);
this.getChildren().add(grid);
this.getStyleClass().add("test");
}//end buttonPanel2
}//end buttonPanel2
CSS:
.buttonV2{
-fx-min-width: 50px;
-fx-min-height:100px;
-fx-max-width: 200px;
-fx-max-height:100px;
-fx-pref-width: 75px;
-fx-pref-height:150px;
}
.buttonH2{
-fx-min-width: 100px;
-fx-min-height:50px;
-fx-max-width: 200px;
-fx-max-height:100px;
-fx-pref-width: 150px;
-fx-pref-height:75px;
}
.button1 {
-fx-min-width: 50px;
-fx-min-height:50px;
-fx-max-width: 100px;
-fx-max-height:100px;
-fx-pref-width: 75px;
-fx-pref-height:75px;
}
.button2 {
-fx-min-width: 100px;
-fx-min-height:100px;
-fx-max-width: 200px;
-fx-max-height:200px;
-fx-pref-width: 150px;
-fx-pref-height:150px;
}
.test{
-fx-background-color: #ff00ff;
}
.test2{
-fx-background-color: #00ffff;
}
Upvotes: 3
Views: 8757
Reputation: 2987
Ok so after some pointers from Jewelsea the answer lies in a combination of using RowConstraints and ColumnConstraints to define the percentage proportion to give each row and column and so set the MaxSize of all the child components of the grid to a value way bigger than you'll ever display (Double.MAX_VALUE).
This makes the layout and its components grow and shrink proportionately as the Stage is resized. The code is below:
package application;
import javafx.application.Application; import javafx.stage.Stage; import javafx.scene.Scene; import javafx.scene.control.Button; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.BorderPane; import javafx.scene.layout.ColumnConstraints; import javafx.scene.layout.GridPane; import javafx.scene.layout.Priority; import javafx.scene.layout.RowConstraints;
JAVA CODE:
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
try {
BorderPane root = new BorderPane();
Scene scene = new Scene(root,400,400);
scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
ButtonPanel2 bp = new ButtonPanel2();
root.setCenter(bp);
primaryStage.setScene(scene);
primaryStage.show();
} catch(Exception e) {
e.printStackTrace();
}
}//end start
public static void main(String[] args) {
launch(args);
}//end main
}//end class main
class ButtonPanel2 extends AnchorPane {
GridPane grid;
Button ba, bb, bc, bd;
/**Construct a new button panel object.**/
public ButtonPanel2(){
//Create Grid and gaps
grid = new GridPane();
grid.setHgap(10);
grid.setVgap(10);
grid.prefWidthProperty().bind(this.widthProperty());
grid.getStyleClass().add("test");
//Init buttons
ba = new Button("A");
bb = new Button("B");
bc = new Button("C");
bd = new Button("D");
ba.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
bb.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
bc.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
bd.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
//Add items to grid.
//Node, colIndex, rowIndex, colSpan, rowSpan
grid.add(ba,0,0,1,1);//
grid.add(bb,1,0,2,1);//
grid.add(bc,0,1,1,2);//
grid.add(bd,1,1,2,2);//
//Grid contstraints.
RowConstraints row1 = new RowConstraints();
row1.setPercentHeight(25);
RowConstraints row2 = new RowConstraints();
row2.setPercentHeight(75);
ColumnConstraints col1 = new ColumnConstraints();
col1.setPercentWidth(25);
ColumnConstraints col2 = new ColumnConstraints();
col2.setPercentWidth(75);
grid.getRowConstraints().addAll(row1,row2);
grid.getColumnConstraints().addAll(col1,col2);
//anchor grid to parent container (anchor)
AnchorPane.setTopAnchor(grid, 0.0);
AnchorPane.setBottomAnchor(grid, 0.0);
AnchorPane.setLeftAnchor(grid, 0.0);
AnchorPane.setRightAnchor(grid, 0.0);
this.getChildren().add(grid);
this.getStyleClass().add("test");
}//end buttonPanel2
}//end buttonPanel2
CSS:
.buttonV2{
-fx-min-width: 50px;
-fx-min-height:100px;
-fx-max-width: 9999px;
-fx-max-height:9999px;
-fx-pref-width: 75px;
-fx-pref-height:150px;
}
.buttonH2{
-fx-min-width: 100px;
-fx-min-height:50px;
-fx-max-width: 200px;
-fx-max-height:100px;
-fx-pref-width: 150px;
-fx-pref-height:75px;
}
.button1 {
-fx-min-width: 50px;
-fx-min-height:50px;
-fx-max-width: 100px;
-fx-max-height:100px;
-fx-pref-width: 75px;
-fx-pref-height:75px;
}
.button2 {
-fx-min-width: 100px;
-fx-min-height:100px;
-fx-max-width: 200px;
-fx-max-height:200px;
-fx-pref-width: 150px;
-fx-pref-height:150px;
}
.test{
-fx-background-color: #ff00ff;
}
.test2{
-fx-background-color: #00ffff;
}
Upvotes: 0
Reputation: 159291
Here is a sample layout created using SceneBuilder, with a couple of preview scenes of varying sizes. You can see that as the size changes, the proportional size of each button stays the same. This is achieved through setting 60:40 constraints on the rows and columns for the GridPane.
The solution also uses a tip for sizing and aligning nodes:
To enable all of the buttons to be resized to the width of the ... pane, the maximum width of each button is set to the Double.MAX_VALUE constant, which enables a control to grow without limit.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<GridPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/8.0.40">
<columnConstraints>
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" percentWidth="40.0" prefWidth="100.0" />
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" prefWidth="100.0" />
</columnConstraints>
<rowConstraints>
<RowConstraints minHeight="10.0" percentHeight="40.0" prefHeight="30.0" vgrow="SOMETIMES" />
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
</rowConstraints>
<children>
<Button maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" mnemonicParsing="false" text="Button" />
<Button maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" mnemonicParsing="false" text="Button" GridPane.columnIndex="1" />
<Button maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" mnemonicParsing="false" text="Button" GridPane.rowIndex="1" />
<Button maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" mnemonicParsing="false" text="Button" GridPane.columnIndex="1" GridPane.rowIndex="1" />
</children>
</GridPane>
The above layout was crafted in about 3 minutes using SceneBuilder from Gluon. I find it a good tool to use to understand various layout types and constraints.
Normally an AnchorPane is for fixed sized layouts. If you want a dynamically sized layout, a different layout pane type is usually preferred, which is why the answer uses only a GridPane and not an AnchorPane.
setting the max size to be essentially Double.MAX_VALUE. I get it but that just feels like such a hack.
Yes, it can seem that way at first, especially because the MAX_VALUE setting is only required for some controls (like buttons), but not for other layouts like StackPanes. But after you getting used to it, it never really bothered me. The kind of rule of thumb is that the layout attempts to do what you might "normally" want. Normally you don't want a button to grow to any size, but for it to remain at its preferred size. Of course that isn't always what you want, which is why you can override defaults to allow unconstrained growth as was demonstrated here.
Upvotes: 3