0
votes

I have written a code using VBox as layout. I want the button to appear at the top row, and then draw 2 horizontal lines which should be at y=200 and 300 in the 400x400 scene. But the output shows the lines at different co-ordinates I give it.

I understand this is because of the Layout I am defining. My question is this:

1) Can I somehow draw lines at true co-ordinates keeping the same layout?

2) If not, which javafx layout would be best for this operation?

3) Assuming there is a MenuBar instead of that button, then which layout would be most suitable?

package practise;

import java.util.Random;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.stage.Stage;

public class Practise extends Application {

    private int c = 0;

    @Override
    public void start(Stage primaryStage) {
        Button btn = new Button();
        VBox root = new VBox();

        Group group = new Group();

        final Line l1 = new Line(0,200,400,200);      
        final Line l2 = new Line(0,300,400,300);      

        btn.setText("Say 'Hello World'");
        btn.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent event) {
                System.out.println("Hello World!");
                }
            }
        );


        l1.setStroke(Color.YELLOW);
        l1.setStrokeWidth(2);
        l1.setLayoutX(0);
        l1.setLayoutY(0);
        l2.setStroke(Color.YELLOW);
        l2.setStrokeWidth(2);
        l2.setLayoutX(0);
        l2.setLayoutY(0);

        group.getChildren().add(l1);
        group.getChildren().add(l2);
        root.getChildren().add(btn);
        root.getChildren().add(group);

        Scene scene = new Scene(root, 400, 400, Color.WHITE);

        primaryStage.setTitle("Hello World!");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

}
2

2 Answers

1
votes

If you want set absolute positions you should use an AnchorPane. Only change VBox to AnchorPane and will work.

1
votes

1) Yes this is possible. Setting the managed property to false results in the parent not layouting the child. This won't make the next node being positioned below the line though.

Button btn = new Button();
VBox root = new VBox();

final Line l1 = new Line(0, 200, 400, 200);
final Line l2 = new Line(0, 300, 400, 300);

l1.setManaged(false);
l2.setManaged(false);

btn.setText("Say 'Hello World'");
btn.setOnAction((ActionEvent event) -> {
    System.out.println("Hello World!");
});

l1.setStroke(Color.YELLOW);
l1.setStrokeWidth(2);
l2.setStroke(Color.YELLOW);
l2.setStrokeWidth(2);
root.getChildren().addAll(btn, l1, l2, rect);

2)

GridPane

allows to specify constraints on the row size.

Button btn = new Button();
Rectangle rect = new Rectangle(400, 10);
GridPane root = new GridPane();
root.getRowConstraints().add(new RowConstraints(200));

final Line l1 = new Line(0, 0, 400, 0);

btn.setText("Say 'Hello World'");
btn.setOnAction((ActionEvent event) -> {
    System.out.println("Hello World!");
});

l1.setStroke(Color.YELLOW);
l1.setStrokeWidth(2);
root.addColumn(0, btn, l1, rect);

SplitPane

could do this too with some adjustments.

Button btn = new Button();
Rectangle rect = new Rectangle(400, 10);

// wrap contents in resizeable Nodes
SplitPane root = new SplitPane(new Pane(btn), new Pane(rect));

// place divider in the middle
root.setDividerPositions(0.5);
root.setOrientation(Orientation.VERTICAL);
root.getStyleClass().add("line-split");
root.getStylesheets().add(getClass().getResource("style.css").toExternalForm());

root.skinProperty().addListener((observable, oldValue, newValue) -> {
    // don't allow resizing
    newValue.getNode().lookupAll(".split-pane-divider").stream().forEach(divider -> divider.setDisable(true));
});

btn.setText("Say 'Hello World'");
btn.setOnAction((ActionEvent event) -> {
    System.out.println("Hello World!");
});

CSS

.line-split .split-pane-divider {
    -fx-min-height: 2;
    -fx-max-height: 2;
    -fx-background-color: yellow;
}

3) MenuBar is a Node too. There seems to be no reason to treat it differently to a Button.