Search code examples
javafxduplicatescontainersnodeschildren

How can I have multiple SplitPanes including children with javaFX?


My structure:

AnchorPane
 | 
 ScrollPane
  |
  FlowPane
   |
   SplitPane
    |
    StackedBarChart
    TableView

How can I, duplicate my SplitPane with its children for a unknown numbers of duplicates?

How can i set for each copy new variable names?

In Scene Builder i right click on SplitPane and select Duplicate. But, the problem is the number of duplicates is not available before writting the program and is not known until runtime.


Solution

  • The best approach for this would be to create a separate FXML for SplitPane, load it as many times as you want and add it to your FlowPane.

    Let us call the new fxml as split.fxml. A controller for the fxml called as SplitController

    Then,we will load this fxml as many times as required, get the root and add it to the FlowPane, which is now present in a separate fxml.

    Let us call the main fxml as container.fxml. A controller for the fxml called as ContainerController

    Answering some of your questions :

    How can I, duplicate my SplitPane with its children for a unknown numbers of duplicates?

    Just load the fxml as many times you need.

    How can i set for each copy new variable names?

    You have the controller, you can identify it with a variable. You may also set a class or id to the splitpane.

    The complete code...

    split.fxml

    <?xml version="1.0" encoding="UTF-8"?>
    
    <?import javafx.scene.chart.CategoryAxis?>
    <?import javafx.scene.chart.NumberAxis?>
    <?import javafx.scene.chart.StackedBarChart?>
    <?import javafx.scene.control.SplitPane?>
    <?import javafx.scene.control.TableColumn?>
    <?import javafx.scene.control.TableView?>
    
    <SplitPane fx:id="splitPane" dividerPositions="0.25" prefHeight="363.0" prefWidth="773.0" fx:controller="SplitController"  xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
        <items>
            <TableView fx:id="tableView" prefHeight="458.0" prefWidth="251.0">
                <columns>
                    <TableColumn prefWidth="75.0" text="C1" />
                    <TableColumn prefWidth="75.0" text="C2" />
                </columns>
            </TableView>
            <StackedBarChart fx:id="stackedBarChart" prefHeight="210.0" prefWidth="107.0">
                <xAxis>
                    <CategoryAxis fx:id="categoryAxis" side="BOTTOM" label="Year" />
                </xAxis>
                <yAxis>
                    <NumberAxis fx:id="numberAxis" side="LEFT" label="Value"/>
                </yAxis>
            </StackedBarChart>
        </items>
    </SplitPane>
    

    SplitController.java

    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    import javafx.fxml.FXML;
    import javafx.fxml.Initializable;
    import javafx.scene.chart.CategoryAxis;
    import javafx.scene.chart.NumberAxis;
    import javafx.scene.chart.StackedBarChart;
    import javafx.scene.chart.XYChart;
    import javafx.scene.control.SplitPane;
    import javafx.scene.control.TableView;
    
    import java.net.URL;
    import java.util.ResourceBundle;
    
    public class SplitController implements Initializable{
    
        @FXML
        private SplitPane splitPane;
    
        @FXML
        private StackedBarChart<String, Number> stackedBarChart;
    
        @FXML
        private TableView<String> tableView;
    
        @FXML
        private CategoryAxis categoryAxis;
    
        @FXML
        private NumberAxis numberAxis;
    
        private ObservableList<String> tableList;
    
        private ObservableList<Integer> chartValueList = FXCollections.observableArrayList();
    
        public void setTableList(ObservableList<String> tableList) {
            this.tableList = FXCollections.observableList(tableList);
        }
    
        public void setChartValueList(ObservableList<Integer> chartValueList) {
            this.chartValueList = FXCollections.observableList(chartValueList);
        }
    
        @Override
        public void initialize(URL location, ResourceBundle resources) {
    
        }
    
        public void setChart(){
            XYChart.Series<String,Number> series1 = new XYChart.Series();
    
            series1.getData().add(new XYChart.Data("January", chartValueList.get(0)));
            series1.getData().add(new XYChart.Data("February", chartValueList.get(1)));
            series1.getData().add(new XYChart.Data("March", chartValueList.get(2)));
            series1.getData().add(new XYChart.Data("April", chartValueList.get(3)));
    
            XYChart.Series<String,Number> series2 = new XYChart.Series();
    
            series2.getData().add(new XYChart.Data("January", chartValueList.get(0)));
            series2.getData().add(new XYChart.Data("February", chartValueList.get(1)));
            series2.getData().add(new XYChart.Data("March", chartValueList.get(2)));
            series2.getData().add(new XYChart.Data("April", chartValueList.get(3)));
    
            stackedBarChart.getData().addAll(series1, series2);
        }
    }
    

    container.fxml

    <?xml version="1.0" encoding="UTF-8"?>
    
    <?import javafx.scene.control.ScrollPane?>
    <?import javafx.scene.layout.BorderPane?>
    <?import javafx.scene.layout.FlowPane?>
    
    <BorderPane xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="ContainerController">
       <center>
            <ScrollPane fx:id="scrollPane" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" BorderPane.alignment="CENTER">
                <content>
                    <FlowPane fx:id="flowPane" maxHeight="-Infinity" maxWidth="-Infinity" prefHeight="353.0" prefWidth="745.0">
    
                    </FlowPane>
                </content>
            </ScrollPane>
       </center>
    </BorderPane>
    

    ContainerController.java

    import javafx.collections.FXCollections;
    import javafx.collections.ObservableList;
    import javafx.fxml.FXML;
    import javafx.fxml.FXMLLoader;
    import javafx.fxml.Initializable;
    import javafx.scene.control.ScrollPane;
    import javafx.scene.control.SplitPane;
    import javafx.scene.layout.AnchorPane;
    import javafx.scene.layout.FlowPane;
    
    import java.io.IOException;
    import java.net.URL;
    import java.util.Random;
    import java.util.ResourceBundle;
    
    public class ContainerController implements Initializable{
    
        @FXML
        private AnchorPane anchorPane;
    
        @FXML
        private ScrollPane scrollPane;
    
        @FXML
        private FlowPane flowPane;
    
        private final int NUMBER_OF_COPIES = 5;
        private final int NUMBER_OF_ROWS = 10;
        private ObservableList<String> tableList = FXCollections.observableArrayList();
        private ObservableList<Integer> chartValueList = FXCollections.observableArrayList();
    
    
        @Override
        public void initialize(URL location, ResourceBundle resources) {
            try {
                for(int i=0; i<NUMBER_OF_COPIES; i++) {
                    FXMLLoader loader = new FXMLLoader(getClass().getResource("/split.fxml"));
                    SplitPane pane = loader.load();
                    SplitController controller = loader.getController();
                    fillChartValueList();
                    controller.setChartValueList(chartValueList);
                    controller.setChart();
                    flowPane.getChildren().add(pane);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        private void fillChartValueList(){
    
            // clear previous values
            chartValueList.clear();
    
            Random rand = new Random();
            int min = 1000;
            int max = 8000;
    
    
            for(int i=0; i<NUMBER_OF_ROWS; i++) {
                int randomNum = rand.nextInt((max - min) + 1) + min;
                chartValueList.add(randomNum);
            }
        }
    }
    

    Main.java - to load the container, set it to a stage and show

    import javafx.application.Application;
    import javafx.fxml.FXMLLoader;
    import javafx.scene.Parent;
    import javafx.scene.Scene;
    import javafx.stage.Stage;
    
    import java.io.IOException;
    
    public class Main extends Application {
    
        public Main() {
            super();
        }
    
        @Override
        public void start(Stage primaryStage) throws IOException{
    
            FXMLLoader loader = new FXMLLoader(getClass().getResource("/container.fxml"));
            Parent parent = loader.load();
            ContainerController controller = loader.getController();
    
            Scene scene = new Scene(parent, 600, 600);
            Stage stage = new Stage();
            stage.setScene(scene);
            stage.show();
    
        }
    
        public static void main(String[] args) {
            launch(args);
        }
    }
    

    Screenshot

    enter image description here

    Note - The TableView is empty as I didn't consider it important to fill it, as per the question.