0

I'm trying to learn JavaFX, I'm building a calculator, and I'm experiencing some button sizing behaviour which I need guidance on.

Firstly, why does the HBox containing the zero and point buttons seem to have a few pixels free on the right hand side? https://i.stack.imgur.com/8imGf.jpg

And secondly, how can I make the equals button take up more or less horizontal space within the HBox that it sits? I've tried setting its preferred width method but this makes no difference. I guess my problem relates to how nodes are sized when there is limited space.

The full class is below. What I have done is create an HBox for each row of numeric buttons and also the point button. These three rows are then placed inside a VBox, which is placed in an HBox next to the equals button.

I'm reading JavaFX for Dummies, and have looked at many posts etc., but can't work it out!

package calculator;

import javafx.application.*;
import static javafx.application.Application.launch;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.*;

public class UserInterface extends Application {

    static int buttonHeight = 30;
    VBox pane;

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {

//4, 5, 6 numeric buttons 
        //Six button
        Button b6 = new Button("6");
        b6.setMaxWidth(Double.MAX_VALUE);
        b6.setPrefHeight(b6.getPrefHeight() + buttonHeight);
        HBox.setHgrow(b6, Priority.ALWAYS);

        //Five button
        Button b5 = new Button("5");
        b5.setMaxWidth(Double.MAX_VALUE);
        b5.setPrefHeight(b5.getPrefHeight() + buttonHeight);
        HBox.setHgrow(b5, Priority.ALWAYS);

        //Four button
        Button b4 = new Button("4");
        b4.setMaxWidth(Double.MAX_VALUE);
        b4.setPrefHeight(b4.getPrefHeight() + buttonHeight);
        HBox.setHgrow(b4, Priority.ALWAYS);

        //Place the buttons in the 456 HBox
        HBox row456 = new HBox();
        row456.getChildren().addAll(b4, b5, b6);

//1, 2, 3 numeric buttons
        //Button three
        Button b3 = new Button("3");
        b3.setMaxWidth(Double.MAX_VALUE);
        b3.setPrefHeight(b3.getPrefHeight() + buttonHeight);
        HBox.setHgrow(b3, Priority.ALWAYS);

        //Button two
        Button b2 = new Button("2");
        b2.setMaxWidth(Double.MAX_VALUE);
        b2.setPrefHeight(b2.getPrefHeight() + buttonHeight);
        HBox.setHgrow(b2, Priority.ALWAYS);

        //Button one
        Button b1 = new Button("1");
        b1.setMaxWidth(Double.MAX_VALUE);
        b1.setPrefHeight(b1.getPrefHeight() + buttonHeight);
        HBox.setHgrow(b1, Priority.ALWAYS);

        //Place the buttons in the 123 HBox
        HBox row123 = new HBox();
        row123.getChildren().addAll(b1, b2, b3);

//Zero and point buttons
        //zero button
        Button zero = new Button("0");
        zero.setMaxWidth(Double.MAX_VALUE);
        zero.setPrefHeight(zero.getPrefHeight() + buttonHeight);
        HBox.setHgrow(zero, Priority.ALWAYS);

        //point button
        Button point = new Button(".");
        point.setMaxWidth(Double.MAX_VALUE);
        point.setPrefHeight(point.getPrefHeight() + buttonHeight);
        HBox.setHgrow(point, Priority.ALWAYS);

        //Place the buttons in the zeroPoint HBox
        HBox zeroPoint = new HBox();
        zeroPoint.setMaxWidth(Double.MAX_VALUE);
        HBox.setHgrow(zeroPoint, Priority.ALWAYS);
        zeroPoint.getChildren().addAll(zero, point);


//Numeric button rows in a VBox
        VBox intLower = new VBox(row456, row123, zeroPoint);
        HBox.setHgrow(intLower, Priority.ALWAYS);


//Equals button
        Button equals = new Button("=");
        HBox.setHgrow(equals, Priority.ALWAYS);
        equals.setMaxWidth(Double.MAX_VALUE);
        equals.setMaxHeight(Double.MAX_VALUE);

// Numeric button VBox and  equals button inside HBox
        HBox lower = new HBox(intLower, equals);
        lower.setAlignment(Pos.BOTTOM_CENTER);


//Populate the root pane
        pane = new VBox();
        pane.getChildren().addAll(lower);
        pane.setAlignment(Pos.BOTTOM_CENTER);

//Add the root pane to a scene
        Scene scene = new Scene(pane, 210, 285);
        // Finish and show the stage
        primaryStage.setResizable(false);
        primaryStage.setScene(scene);
        primaryStage.setTitle("Calculator");
        primaryStage.show();

    }
}

Edit: I've now tried using a GridPane to assemble this GUI, and am having the same problem with rows not aligning properly and small spaces visible between nodes - it looks like when you take the cheapest quote to renovate a kitchen and later find that none of the cupboards close flush: https://i.stack.imgur.com/r47K1.jpg

package calculator;

import javafx.application.*;
import static javafx.application.Application.launch;
import javafx.geometry.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.*;

public class UserInterface extends Application {

    private static int butHeightModifier = 30;
    private static int appWidth = 225 * 2;
    private static int appHeight = 310 * 2;
    private static int numRows = 7;
    private static int numCols = 5;

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
//7, 8, 9 numeric buttons 
        //Nine button
        Button b9 = new Button("9");
        b9.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        HBox.setHgrow(b9, Priority.ALWAYS);
        VBox.setVgrow(b9, Priority.ALWAYS);

        //Eight button
        Button b8 = new Button("8");
        b8.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        HBox.setHgrow(b8, Priority.ALWAYS);
        VBox.setVgrow(b8, Priority.ALWAYS);

        //Seven button
        Button b7 = new Button("7");
        b7.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        HBox.setHgrow(b7, Priority.ALWAYS);
        VBox.setVgrow(b7, Priority.ALWAYS);

        //Place the buttons in the 456 HBox
        HBox row789 = new HBox();
        row789.getChildren().addAll(b7, b8, b9);

//4, 5, 6 numeric buttons 
        //Six button
        Button b6 = new Button("6");
        b6.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        HBox.setHgrow(b6, Priority.ALWAYS);
        VBox.setVgrow(b6, Priority.ALWAYS);

        //Five button
        Button b5 = new Button("5");
        b5.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        HBox.setHgrow(b5, Priority.ALWAYS);
        VBox.setVgrow(b5, Priority.ALWAYS);

        //Four button
        Button b4 = new Button("4");
        b4.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        HBox.setHgrow(b4, Priority.ALWAYS);
        VBox.setVgrow(b4, Priority.ALWAYS);

        //Place the buttons in the 456 HBox
        HBox row456 = new HBox();
        row456.getChildren().addAll(b4, b5, b6);

//1, 2, 3 numeric buttons
        //Button three
        Button b3 = new Button("3");
        b3.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        HBox.setHgrow(b3, Priority.ALWAYS);
        VBox.setVgrow(b3, Priority.ALWAYS);

        //Button two
        Button b2 = new Button("2");
        b2.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        HBox.setHgrow(b2, Priority.ALWAYS);
        VBox.setVgrow(b2, Priority.ALWAYS);

        //Button one
        Button b1 = new Button("1");
        b1.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        HBox.setHgrow(b1, Priority.ALWAYS);
        VBox.setVgrow(b1, Priority.ALWAYS);

        //Place the buttons in the 123 HBox
        HBox row123 = new HBox();
        row123.getChildren().addAll(b1, b2, b3);

//Zero and point buttons
        //zero button
        Button zero = new Button("0");
        zero.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        HBox.setHgrow(zero, Priority.ALWAYS);
        VBox.setVgrow(zero, Priority.ALWAYS);

        //point button
        Button point = new Button(".");
        point.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        HBox.setHgrow(point, Priority.ALWAYS);
        VBox.setVgrow(point, Priority.ALWAYS);

        //Place the buttons in the zeroPoint HBox
        HBox zeroPoint = new HBox();
        zeroPoint.getChildren().addAll(zero, point);

//Multiply and division buttons
        Button multiply = new Button("X");
        multiply.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        HBox.setHgrow(multiply, Priority.ALWAYS);
        VBox.setVgrow(multiply, Priority.ALWAYS);

        Button division = new Button("/");
        division.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        HBox.setHgrow(division, Priority.ALWAYS);
        VBox.setVgrow(division, Priority.ALWAYS);

        //Place the buttons in the zeroPoint HBox
        HBox multiDiv = new HBox();
        multiDiv.getChildren().addAll(multiply, division);

//Plus and minus buttons
        Button plus = new Button("+");
        plus.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        HBox.setHgrow(plus, Priority.ALWAYS);
        VBox.setVgrow(plus, Priority.ALWAYS);

        Button minus = new Button("-");
        minus.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        HBox.setHgrow(minus, Priority.ALWAYS);
        VBox.setVgrow(minus, Priority.ALWAYS);

        //Place the buttons in the zeroPoint HBox
        HBox plusMinus = new HBox();
        plusMinus.getChildren().addAll(plus, minus);

//Equals button
        Button equals = new Button("=");
        equals.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);

// Create GridPane
        GridPane grid = new GridPane();

// Set row and column constraints
        ColumnConstraints col = new ColumnConstraints();
        col.setPercentWidth(20);
        grid.getColumnConstraints().addAll(col, col, col, col, col);

        RowConstraints row = new RowConstraints();
        row.setPercentHeight(14.2857);
        grid.getRowConstraints().addAll(row, row, row, row, row, row, row);

//Set filling constraint
        for (int r = 0; r <= numRows; r++) {
            RowConstraints rc = new RowConstraints();
            rc.setFillHeight(true);
            rc.setVgrow(Priority.ALWAYS);
            grid.getRowConstraints().add(rc);
        }
        for (int c = 0; c <= numCols; c++) {
            ColumnConstraints cc = new ColumnConstraints();
            cc.setFillWidth(true);
            cc.setHgrow(Priority.ALWAYS);
            grid.getColumnConstraints().add(cc);
        }

//Add nodes to grid pane
        grid.add(zeroPoint, 0, 6, 3, 1);
        grid.add(equals, 3, 5, 2, 2);
        grid.add(row123, 0, 5, 3, 1);
        grid.add(row456, 0, 4, 3, 1);
        grid.add(row789, 0, 3, 3, 1);
        grid.add(multiDiv, 3, 4, 2, 1);
        grid.add(plusMinus, 3, 3, 2, 1);

// Create the scene and the stage
        Scene scene = new Scene(grid);
        primaryStage.setScene(scene);
        primaryStage.setTitle("Calculator");
        primaryStage.setMinWidth(appWidth);
        primaryStage.setMinHeight(appHeight);
        primaryStage.setResizable(false);
        primaryStage.show();
    }
}
marc_s
  • 732,580
  • 175
  • 1,330
  • 1,459
ki81
  • 75
  • 1
  • 6
  • Probably better to use a `GridPane` for the `Buttons`. – SedJ601 May 05 '18 at 01:02
  • I'll definitely give GridPane a shot - I have yet to read that chapter. But I'd still like to know why the HBox/VBox combinations act like this. – ki81 May 05 '18 at 01:33
  • I created a `GridPane` layout but I am going to hold back on posting it unless you run into new problems. – SedJ601 May 05 '18 at 01:36
  • Also, I personally would use `SceneBuilder` for a static layout. – SedJ601 May 05 '18 at 02:10
  • Ok thanks for the advice. I'll experiment and come back with any questions. – ki81 May 05 '18 at 03:05
  • Ok, I tried GridPane and get the same problem. Any ideas? I've edited my post with the new code. – ki81 May 05 '18 at 09:12
  • When I said to use `GridPane`, I meant you should get rid of your row `HBoxes` and used `GridPane` instead – SedJ601 May 05 '18 at 13:50

1 Answers1

1

The GridLines are showing so that you can see what's going on with Buttons like the equal and decimal buttons.

The Equal Button extends more than one row and the Decimal Button extends more than one column

buttonLayout.add(buttonList.get(11), 3, 0, 1, 4);//Equal Button
buttonLayout.add(buttonList.get(10), 1, 3, 2, 1);//Decimal Button

add(Node child, int columnIndex, int rowIndex, int colspan, int rowspan) Adds a child to the gridpane at the specified column,row position and spans.

This code allows the Buttons to fill the Cells. code from here.

    for (int rowIndex = 0; rowIndex < 4; rowIndex++) {
        RowConstraints rc = new RowConstraints();
        rc.setVgrow(Priority.ALWAYS) ; // allow row to grow
        rc.setFillHeight(true); // ask nodes to fill height for row
        // other settings as needed...
        buttonLayout.getRowConstraints().add(rc);
    }
    for (int colIndex = 0; colIndex < 4; colIndex++) {
        ColumnConstraints cc = new ColumnConstraints();
        cc.setHgrow(Priority.ALWAYS) ; // allow column to grow
        cc.setFillWidth(true); // ask nodes to fill space for column
        // other settings as needed...
        buttonLayout.getColumnConstraints().add(cc);
    }

Full Code:

import java.util.ArrayList;
import java.util.List;
import javafx.application.*;
import static javafx.application.Application.launch;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.*;

public class UserInterface extends Application {

    VBox root = new VBox();
    MenuBar menuBar = new MenuBar();
    TextField display = new TextField("");
    List<Button> buttonList = new ArrayList();
    GridPane buttonLayout = new GridPane();


    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage){

        for(int i = 0; i < 10; i++)
        {
            Button tempButton = new Button(Integer.toString(i));            
            buttonList.add(tempButton);
        }        
        buttonList.add(new Button("."));
        buttonList.add(new Button("="));    


        for(int i = 0; i < buttonList.size(); i++)
        {
            buttonList.get(i).setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        }

        buttonLayout.add(buttonList.get(7), 0, 0);
        buttonLayout.add(buttonList.get(8), 1, 0);
        buttonLayout.add(buttonList.get(9), 2, 0);
        buttonLayout.add(buttonList.get(11), 3, 0, 1, 4);


        buttonLayout.add(buttonList.get(4), 0, 1);
        buttonLayout.add(buttonList.get(5), 1, 1);
        buttonLayout.add(buttonList.get(6), 2, 1);

        buttonLayout.add(buttonList.get(1), 0, 2);
        buttonLayout.add(buttonList.get(2), 1, 2);
        buttonLayout.add(buttonList.get(3), 2, 2);

        buttonLayout.add(buttonList.get(0), 0, 3);
        buttonLayout.add(buttonList.get(10), 1, 3, 2, 1);


        buttonLayout.setGridLinesVisible(true);
        buttonLayout.setMaxSize(Double.MAX_VALUE, Double.MAX_VALUE);
        for (int rowIndex = 0; rowIndex < 4; rowIndex++) {
            RowConstraints rc = new RowConstraints();
            rc.setVgrow(Priority.ALWAYS) ; // allow row to grow
            rc.setFillHeight(true); // ask nodes to fill height for row
            // other settings as needed...
            buttonLayout.getRowConstraints().add(rc);
        }
        for (int colIndex = 0; colIndex < 4; colIndex++) {
            ColumnConstraints cc = new ColumnConstraints();
            cc.setHgrow(Priority.ALWAYS) ; // allow column to grow
            cc.setFillWidth(true); // ask nodes to fill space for column
            // other settings as needed...
            buttonLayout.getColumnConstraints().add(cc);
        }

        VBox.setVgrow(buttonLayout, Priority.ALWAYS);
        root.getChildren().addAll(display, buttonLayout);


        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        primaryStage.show();        
    }
}

enter image description here

SedJ601
  • 12,173
  • 3
  • 41
  • 59
  • Thanks for this. When I use a GridPane by itself the buttons do align properly. However, what if I need to have two buttons span three cells? Doesn't this require an HBox? I really want to know why HBox fails to align. Button, HBox, and GridPane are all nodes - when it comes to alignment, shouldn't they all be treated the same? – ki81 May 06 '18 at 03:06
  • I specifically point out two situations in my example where the column or row span more than one. The equal button span 4 rows and the decimal button span 2. – SedJ601 May 06 '18 at 03:07
  • I see that. My question was how to get 2 buttons to span 3 cells. I realise now that this can be achieved by doubling the number of columns and having each button occupy 3 cells, but that still doesn't explain why HBox's fail to align. I'm not trying to disparage your solution, because it works, but I also want to try and understand why HBox and VBox are behaving as they are. – ki81 May 06 '18 at 04:52
  • Unfortunately, I don't want to go through your code to figure it out. Based on your original picture, I think your layout should have an `HBox` as the root. You should have added a `VBox` and a `Button`(Equal Button) to the root. In your `VBox`, you should have added your three `HBox` with the `Buttons`. This layout should solve your gap problem. – SedJ601 May 06 '18 at 04:59