I have my current class assignment and I'm having trouble setting the margin for my rectangle. It is a fruit list that is supposed to calculate the total of the desired fruits picked by the user. the HBox is used to display the bottom part of the window where I have my checkout button as well as my total label in the center of the rectangle, which is so far working just fine. But, I'm trying to set the margin for the rectangle so it looks more centered in the bottom part and I keep running into this set margin static error. Please help, and would love it if anybody can show me how to set an exception for my total if the user does not want to pick from my fruits options where the total = 0 for the non-picked ones. Also, please let me know if there are some cleaner way of coding, would love to get to know that as well :)

public class Grid_Layout extends Application {

    public static void main(String[] args) {
        Application.launch(args);
    }
    
    public void start(Stage primaryStage) 
    {   
        
        BorderPane border = new BorderPane(); // for the title use 
        HBox hbox = new HBox(20); // for the rectangle, checkout button, and the total label         // 20 for spacing      
        
        
        // Calling out GridPane and setting alignments and measurements
        GridPane pane = new GridPane();
        border.setCenter(pane);
        border.setBottom(hbox);
        pane.setAlignment(Pos.CENTER);
        pane.setPadding(new Insets(10,20,10,20)); //top, right, bottom, left space between content and border
        pane.setHgap(10); 
        pane.setVgap(20); 
        pane.setStyle("-fx-background-color: #e6ffb3;");
        
        // Set images as Image
        
        Image apple_img = new Image("File:images/apple.png");
        Image apricot_img = new Image("File:images/apricot.png");
        Image banana_img = new Image("File:images/banana.png");
        Image cherry_img = new Image("File:images/cherry.png");
        Image grape_img = new Image("File:images/grape.png");
        Image lemmon_img = new Image("File:images/lemmon.png");
        Image orange_img = new Image("File:images/orange.png");
        Image pear_img = new Image("File:images/pear.png");
        Image strawberry_img = new Image("File:images/strawberry.png");
        Image watermelon_img = new Image("File:images/watermelon.png");
        
        // To set Image(s) to ImageView in order to display
        
        ImageView apple_iv = new ImageView(apple_img);
        pane.add(apple_iv, 0, 1);
        
        ImageView apricot_iv = new ImageView(apricot_img);
        pane.add(apricot_iv, 3, 1);
        
        ImageView banana_iv = new ImageView(banana_img);
        pane.add(banana_iv, 6, 1);
        
        ImageView cherry_iv = new ImageView(cherry_img);
        pane.add(cherry_iv, 0, 2);
        
        ImageView grape_iv = new ImageView(grape_img);
        pane.add(grape_iv, 3, 2);
        
        ImageView lemmon_iv = new ImageView(lemmon_img);
        pane.add(lemmon_iv, 6, 2);
        
        ImageView orange_iv = new ImageView(orange_img);
        pane.add(orange_iv, 0, 3);
        
        ImageView pear_iv = new ImageView(pear_img);
        pane.add(pear_iv, 3, 3);
        
        ImageView strawberry_iv = new ImageView(strawberry_img);
        pane.add(strawberry_iv, 6, 3);
        
        ImageView watermelon_iv = new ImageView(watermelon_img);
        pane.add(watermelon_iv, 0, 4);
        
        
        
        // To insert all of the textfields for user input
        
        TextField tf1 = new TextField();
        tf1.setMaxWidth(50);
        TextField tf2 = new TextField();
        tf2.setMaxWidth(50);
        TextField tf3 = new TextField();
        tf3.setMaxWidth(50);
        TextField tf4 = new TextField();
        tf4.setMaxWidth(50);
        TextField tf5 = new TextField();
        tf5.setMaxWidth(50);
        TextField tf6 = new TextField();
        tf6.setMaxWidth(50);
        TextField tf7 = new TextField();
        tf7.setMaxWidth(50);
        TextField tf8 = new TextField();
        tf8.setMaxWidth(50);
        TextField tf9 = new TextField();
        tf9.setMaxWidth(50);
        TextField tf10 = new TextField();
        tf10.setMaxWidth(50);
        
        // To insert all of the labels
        
        pane.add(new Label("0.99/lb"), 1, 1);
        pane.add(tf1, 2, 1);
        pane.add(new Label("1.49/lb"), 4, 1);
        pane.add(tf2, 5, 1);
        pane.add(new Label("0.49/lb"), 7, 1);
        pane.add(tf3, 8, 1);
        pane.add(new Label("1.99/lb"), 1, 2);
        pane.add(tf4, 2, 2);
        pane.add(new Label("0.99/lb"), 4, 2);
        pane.add(tf5, 5, 2);
        pane.add(new Label("1.99/lb"), 7, 2);
        pane.add(tf6, 8, 2);
        pane.add(new Label("0.99/lb"), 1, 3);
        pane.add(tf7, 2, 3);
        pane.add(new Label("1.49/lb"), 4, 3);
        pane.add(tf8, 5, 3);
        pane.add(new Label("1.99/lb"), 7, 3);
        pane.add(tf9, 8, 3);
        pane.add(new Label("0.99/lb"), 1, 4);
        pane.add(tf10, 2, 4);
        
        // to lock in prefered window size for the user, it is still resizable
        
        ColumnConstraints col = new ColumnConstraints();
        col.setPercentWidth(10);
        pane.setPrefSize(700, 350);
        pane.setMaxSize(Region.USE_COMPUTED_SIZE, Region.USE_COMPUTED_SIZE);
        
        
        // Body title
        
        Label titlelbl = new Label("Anna's Fresh Market");
        border.setTop(titlelbl);
        BorderPane.setAlignment(titlelbl, Pos.CENTER);
        titlelbl.setPadding(new Insets(10));
        titlelbl.setStyle("-fx-font-size: 25px; -fx-text-fill: green;-fx-background-color: #e6ffb3;");
        border.setStyle("-fx-background-color: #e6ffb3;");
        
        // Rectangle

        
        Rectangle rect = new Rectangle();
        hbox.setAlignment(Pos.BOTTOM_CENTER);
        
        hbox.setStyle("-fx-border-color: green;");
        rect.setFill(Color.TRANSPARENT);
        
        hbox.setPadding(new Insets(10,10,10,10));
        hbox.setMargin(rect, new Insets(20,30,40,50));
        
        // add result label & checkout button
        
        Label result = new Label("Total: $0.00");
        result.setAlignment(Pos.CENTER);
        Button btAdd = new Button("Checkout");
        hbox.getChildren().addAll(rect, result, btAdd);
        
        
        
        btAdd.setOnAction(e ->{
            Double AppleValue = Double.valueOf(tf1.getText());
            Double AppricotValue = Double.valueOf(tf2.getText());
            Double BananaValue = Double.valueOf(tf3.getText());
            Double CherryValue = Double.valueOf(tf4.getText());
            Double GrapeValue = Double.valueOf(tf5.getText());
            Double LemonValue = Double.valueOf(tf6.getText());
            Double OrangeValue = Double.valueOf(tf7.getText());
            Double PearValue = Double.valueOf(tf8.getText());
            Double StrawberryValue = Double.valueOf(tf9.getText());
            Double WatermelonValue = Double.valueOf(tf10.getText());
            
            
            Double r = (AppleValue * 0.99) + (AppricotValue * 1.49) + (BananaValue * 0.49) + 
                    (CherryValue * 1.99) + (GrapeValue * 0.99)+ (LemonValue * 1.99)+ 
                    (OrangeValue * 0.99)+ (PearValue * 1.49)+ (StrawberryValue * 1.99) + 
                    (WatermelonValue * 0.99);
            result.setText("$ " + r.toString());
            
        });
        
        
        // Window title 
        Scene scene = new Scene(border);
        
        primaryStage.setTitle("Anna's Fresh Market"); 
        primaryStage.setScene(scene);
        primaryStage.setResizable(true);
        primaryStage.show();
        
    }   
}
1

There are 1 answers

1
DaveB On

Since you asked about "cleaner coding", I have these suggestions:

  • Don't instantiate variables that you are going to only use once, inline them
  • When you have repeated lines of almost the same code, put it into a method
  • Don't include meaningless comments
  • Use meaningful variable names

I couldn't figure out what rect was for since you created it with no size, but I put the results in an HBox and gave it a border, in case that was what you were going for.

You created ColumnConstraints, but didn't use them.

Just sorting out those issues, cuts your code by nearly 1/2 and makes it a lot easier to follow (I had to change all your images to "flag" since I didn't have your image files):

public class AnnasMarket1 extends Application {

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

    public void start(Stage primaryStage) {

        BorderPane border = new BorderPane();
        HBox hbox = new HBox(20);

        GridPane pane = new GridPane();
        border.setCenter(pane);
        border.setBottom(hbox);
        pane.setAlignment(Pos.CENTER);
        pane.setPadding(new Insets(10, 20, 10, 20));
        pane.setHgap(10);
        pane.setVgap(20);
        pane.setStyle("-fx-background-color: #e6ffb3;");

        TextField tfApple = createQuantityTextField();
        TextField tfApricot = createQuantityTextField();
        TextField tfBanana = createQuantityTextField();
        TextField tfCherry = createQuantityTextField();
        TextField tfGrape = createQuantityTextField();
        TextField tfLemon = createQuantityTextField();
        TextField tfOrange = createQuantityTextField();
        TextField tfPear = createQuantityTextField();
        TextField tfStrawberry = createQuantityTextField();
        TextField tfWatermelon = createQuantityTextField();

        addFruit(pane, "/images/flag.png", tfApple, 0, 1, "0.99/lb");
        addFruit(pane, "/images/flag.png", tfApricot, 3, 1, "1.49/lb");
        addFruit(pane, "/images/flag.png", tfBanana, 6, 1, "0.49/lb");
        addFruit(pane, "/images/flag.png", tfCherry, 0, 2, "1.99/lb");
        addFruit(pane, "/images/flag.png", tfGrape, 3, 2, "0.99/lb");
        addFruit(pane, "/images/flag.png", tfLemon, 6, 2, "1.99/lb");
        addFruit(pane, "/images/flag.png", tfOrange, 0, 3, "0.99/lb");
        addFruit(pane, "/images/flag.png", tfPear, 3, 3, "1.49/lb");
        addFruit(pane, "/images/flag.png", tfStrawberry, 6, 3, "1.99/lb");
        addFruit(pane, "/images/flag.png", tfWatermelon, 0, 4, "0.99/lb");

        ColumnConstraints col = new ColumnConstraints();
        col.setPercentWidth(10);
        pane.setPrefSize(700, 350);
        pane.setMaxSize(Region.USE_COMPUTED_SIZE, Region.USE_COMPUTED_SIZE);


        Label titleLbl = new Label("Anna's Fresh Market");
        border.setTop(titleLbl);
        BorderPane.setAlignment(titleLbl, Pos.CENTER);
        titleLbl.setPadding(new Insets(10));
        titleLbl.setStyle("-fx-font-size: 25px; -fx-text-fill: green;-fx-background-color: #e6ffb3;");
        border.setStyle("-fx-background-color: #e6ffb3;");

        hbox.setAlignment(Pos.BOTTOM_CENTER);

        hbox.setStyle("-fx-border-color: green;");
        hbox.setPadding(new Insets(10, 10, 10, 10));
        Label result = new Label("Total: $0.00");
        result.setAlignment(Pos.CENTER);
        HBox resultBox = new HBox(result);
        resultBox.setFillHeight(false);
        resultBox.setPadding(new Insets(6));
        resultBox.setStyle("-fx-border-color: green;");
        Button btAdd = new Button("Checkout");
        hbox.getChildren().addAll(resultBox, btAdd);


        btAdd.setOnAction(e -> {
            double r =
                    (Double.parseDouble(tfApple.getText()) * 0.99) + (Double.parseDouble(tfApricot.getText()) * 1.49) + (Double.parseDouble(
                            tfBanana.getText()) * 0.49) + (Double.parseDouble(tfCherry.getText()) * 1.99) + (Double.parseDouble(tfGrape.getText()) * 0.99) + (Double
                            .parseDouble(tfLemon.getText()) * 1.99) + (Double.parseDouble(tfOrange.getText()) * 0.99) + (Double.parseDouble(
                            tfPear.getText()) * 1.49) + (Double.parseDouble(tfStrawberry.getText()) * 1.99) + (Double.parseDouble(
                            tfWatermelon.getText()) * 0.99);
            result.setText("Total $" + Double.toString(r));

        });
        primaryStage.setTitle("Anna's Fresh Market");
        primaryStage.setScene(new Scene(border));
        primaryStage.setResizable(true);
        primaryStage.show();

    }

    private void addFruit(GridPane pane, String imageLocation, TextField textField, int baseColumn, int row, String labelText) {
        pane.add(new ImageView(new Image(imageLocation)), baseColumn, row);
        pane.add(new Label(labelText), baseColumn + 1, row);
        pane.add(textField, baseColumn + 2, row);
    }

    private TextField createQuantityTextField() {
        TextField results = new TextField("0");
        results.setMaxWidth(50);
        return results;
    }
}

The biggest issue I see is that you're treating the whole screen like one big monolithic structure. Your GridPane has 30 individual elements in it, but it's actual just 10 smaller structures each with three elements in it. You can avoid a ton of repetition and make the structure much more coherent by creating a custom widget (I used an extension of HBox that I called "FruitBox") to hold the your Image/Label/TextField triplets and then just put those in the pane.

Once you've rethought it from 30 widgets in a GridPane to just 10, you should realize that FlowPane is a better layout container than GridPane. Then you don't need to keep track of rows and columns.

Next, you've got the prices hard-coded as String and Double and mingled up with your layout. I've treated them more like data, and the layout for the FruitBox converts it to a String for the display. FruitBox is the only thing that has a reference to the TextField, and it has a method called getAmount() that will multiply the price times the number in the TextField.

Even though the price/image info is still hard-coded, it's now treated a little more like data now.

Finally, it's easier to see how things go together when you split out all of the setup work for each section of the BorderPane into their own methods. It also makes it easier to get away from the monolithic design when you break it down this way.

If it was me, I'd get rid of all of the in-line styling and create a CSS for the scene and use it.

This is my refactoring into a non-monolithic design. I hope you find it interesting, if not useful:

public class AnnasMarket2 extends Application {
    private ObservableList<FruitBox> fruitBoxes = FXCollections.observableArrayList();

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

    public void start(Stage primaryStage) {
        BorderPane border = new BorderPane();
        border.setTop(setUpTitle());
        border.setBottom(setUpButtonHBox());

        FlowPane flowPane = createFlowPane();
        border.setCenter(flowPane);
        createFruitBoxes();
        flowPane.getChildren().addAll(fruitBoxes);
        border.setStyle("-fx-background-color: #e6ffb3;");
        primaryStage.setTitle("Anna's Fresh Market");
        primaryStage.setScene(new Scene(border));
        primaryStage.setResizable(true);
        primaryStage.show();

    }


    @NotNull
    private FlowPane createFlowPane() {
        FlowPane pane = new FlowPane();
        pane.setAlignment(Pos.CENTER);
        pane.setPadding(new Insets(10, 20, 10, 20));
        pane.setStyle("-fx-background-color: #e6ffb3;");
        return pane;
    }

    private Node setUpTitle() {
        Label results = new Label("Anna's Fresh Market");
        BorderPane.setAlignment(results, Pos.CENTER);
        results.setPadding(new Insets(10));
        results.setStyle("-fx-font-size: 25px; -fx-text-fill: green;-fx-background-color: #e6ffb3;");
        return results;
    }

    private Node setUpButtonHBox() {
        HBox hBox = new HBox(20);
        hBox.setAlignment(Pos.BOTTOM_CENTER);

        hBox.setStyle("-fx-border-color: green;");
        hBox.setPadding(new Insets(10, 10, 10, 10));
        Label result = new Label("Total: $0.00");
        result.setAlignment(Pos.CENTER);
        HBox resultBox = new HBox(result);
        resultBox.setFillHeight(false);
        resultBox.setPadding(new Insets(6));
        resultBox.setStyle("-fx-border-color: green;");
        Button btAdd = new Button("Checkout");
        hBox.getChildren().addAll(resultBox, btAdd);
        btAdd.setOnAction(e -> {
            double r = fruitBoxes.stream().map(FruitBox::getAmount).reduce(0d, (a, b) -> a + b);
            result.setText("Total $" + Double.toString(r));
        });
        return hBox;
    }

    private void createFruitBoxes() {
        fruitBoxes.add(new FruitBox("/images/flag.png", 0.99));
        fruitBoxes.add(new FruitBox("/images/flag.png", 0.99));
        fruitBoxes.add(new FruitBox("/images/flag.png", 1.49));
        fruitBoxes.add(new FruitBox("/images/flag.png", 0.49));
        fruitBoxes.add(new FruitBox("/images/flag.png", 1.99));
        fruitBoxes.add(new FruitBox("/images/flag.png", 0.99));
        fruitBoxes.add(new FruitBox("/images/flag.png", 1.99));
        fruitBoxes.add(new FruitBox("/images/flag.png", 0.99));
        fruitBoxes.add(new FruitBox("/images/flag.png", 1.49));
        fruitBoxes.add(new FruitBox("/images/flag.png", 1.99));
        fruitBoxes.add(new FruitBox("/images/flag.png", 0.99));
    }

    class FruitBox extends HBox {

        private TextField textField = new TextField("0");
        private double price;

        public FruitBox(String imageLocation, double price) {
            this.price = price;
            textField.setMaxWidth(50);

            ImageView imageView = new ImageView(new Image(imageLocation));
            imageView.setFitHeight(70);
            imageView.setFitWidth(70);
            setAlignment(Pos.CENTER_LEFT);
            setSpacing(6);
            setPadding(new Insets(10));
            getChildren().addAll(imageView, new Text("$" + price + "/lb"), textField);
        }

        public double getAmount() {
            return price * Double.parseDouble(textField.getText());
        }

    }
}