Search code examples
javajavafxfxml

JavaFX Container Structure Scrollpane


The GridPane i need for my GameBoard (Which should hold the amount of "tiles" my simulator terrain has) does not properly fit into a ScrollPane.

Shifting around the hierarchy and working with colorized containers to identify the issue was no help to fix the issue itself.

The Main Container FXML where the GameBoard is included


    <SplitPane dividerPositions="0.5" BorderPane.alignment="CENTER">
                    <items>
                        <AnchorPane>
                            <children>
                                <TextArea layoutX="1.0" layoutY="-14.0" AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0" />
                            </children>
                        </AnchorPane>
                      <ScrollPane prefWidth="800" prefHeight="500" hbarPolicy="ALWAYS" vbarPolicy="ALWAYS" >

                      <GameBoardCC fx:id="gameBoard" > 

                      </ScrollPane>
                    </items>
                </SplitPane>

The Custom Control itself:


<fx:root xmlns:fx="http://javafx.com/fxml/1" type="AnchorPane" xmlns="http://javafx.com/javafx/8.0.221">

  <GridPane fx:id="gameBoard"
            style="-fx-background-color: green">
  </GridPane>

</fx:root>

Basically the code that adds these items, I'm pretty sure I got most of it from Stackoverflow

 for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                // create node
                TerrainNode node = new TerrainNode ("Item " + i + "/" + j, i * 100, j *100, 100, 100);
                node.setOpacity (0.5);
                // add node to group
                gameBoard.getChildren ( ).add (node);
            }
        }

A Picture of the Slider Behaviour, for some reason these nodes don't get added INTO the Gridpane, but rather outside of it?

Pictures of the Slider Situation

I was hoping to have the slider to be "stuck" at 50% (though this is not my main concern right now) and since my simulation is supposed to have a changeable grid size of tiles with fixed tile sizes, the scrollbars should appear at for instance 20x20 grids or in future when the client gets too small.


Solution

  • As @fabian specified, let the GridPane do its stuff. Each layout pane provided by JavaFX has a specific feature. I suggest you go through different types of layout panes and understand their basic purpose. Refer to all green boxes in below image :) enter image description here

    Ofcourse the layout you choosed(GridPane) is the correct one . But the execution is not correct. GridPane layouts its children based on the provided column/row indexes. You dont need to handle there layout positioning.

    Below is a quick example of what the idea is like:

    import javafx.application.Application;
    import javafx.geometry.Insets;
    import javafx.scene.Scene;
    import javafx.scene.control.Label;
    import javafx.scene.control.RadioButton;
    import javafx.scene.control.ScrollPane;
    import javafx.scene.control.ToggleGroup;
    import javafx.scene.layout.GridPane;
    import javafx.scene.layout.Priority;
    import javafx.scene.layout.StackPane;
    import javafx.scene.layout.VBox;
    import javafx.scene.paint.Color;
    import javafx.scene.shape.Rectangle;
    import javafx.stage.Stage;
    
    import java.util.stream.Stream;
    
    public class GameBoardDemo extends Application {
        @Override
        public void start(Stage primaryStage) throws Exception {
            ToggleGroup tg = new ToggleGroup();
            RadioButton size1 = new RadioButton("5 X 5");
            RadioButton size2 = new RadioButton("5 X 10");
            RadioButton size3 = new RadioButton("10 X 10");
            RadioButton size4 = new RadioButton("10 X 15");
            RadioButton size5 = new RadioButton("15 X 15");
            Stream.of(size1, size2, size3, size4, size5).forEach(rb -> {
                rb.setToggleGroup(tg);
                rb.setId(rb.getText());
            });
            GridPane options = new GridPane();
            options.setHgap(10);
            options.addRow(0, size1, size2, size3, size4, size5);
    
            GridPane board = new GridPane();
            board.setPrefSize(800, 500);
            ScrollPane scrollBoard = new ScrollPane(board);
            VBox.setVgrow(scrollBoard, Priority.ALWAYS);
    
            tg.selectedToggleProperty().addListener((obs, old, val) -> {
                RadioButton rb = (RadioButton) val;
                double rows, columns;
                switch (rb.getText()) {
                    case "5 X 5":
                        rows = 5;
                        columns = 5;
                        break;
                    case "5 X 10":
                        rows = 5;
                        columns = 10;
                        break;
                    case "10 X 10":
                        rows = 10;
                        columns = 10;
                        break;
                    case "10 X 15":
                        rows = 10;
                        columns = 15;
                        break;
                    default:
                        rows = 15;
                        columns = 15;
                }
                board.getChildren().clear();
                for (int rowIndex = 0; rowIndex < rows; rowIndex++) {
                    for (int columnIndex = 0; columnIndex < columns; columnIndex++) {
                        Rectangle node = new Rectangle(100, 100);
                        node.setFill(Color.LIGHTBLUE);
                        node.setStroke(Color.BLACK);
                        StackPane terrainNode = new StackPane(node, new Label("Item " + rowIndex + "-" + columnIndex));
                        board.add(terrainNode, columnIndex, rowIndex);
                    }
                }
            });
            VBox root = new VBox();
            root.setPadding(new Insets(10));
            root.setSpacing(15);
            root.getChildren().addAll(options, scrollBoard);
    
            Scene scene = new Scene(root);
            primaryStage.setScene(scene);
            primaryStage.setTitle("BoardGame");
            primaryStage.show();
        }
    }