Search code examples
javajavafxfxmlscenebuilderscene

Dynamically add widgets on a scene through fxml or Scene builder in JavaFX


I am working on a simple BeatBox, where different sounds play depending on the checkboxes that were selected.

The program is supposed to have 256 checkboxes, displayed on a 16x16 grid. So when I build the GUI through direct code (i.e without fxml or the scene builder), I can easily create a simple loop to create the 256 checkboxes, add them to a list so that I can use them later, and add them to the grid.

I am trying to change that approach and do it with the scene builder or coding directly on the fxml file, but I can't figure out a way of doing it, and I don't even know if that is possible. Trying to figure out that, I came across this doubt: - Is there a way to create a list of some type of widget, populated with loads of components (for my given example, a list with 256 CheckBoxes), through fxml or scene builder, and then have a reference to the list on my Controller class?

I really don't like the idea of dragging-and-dropping 256 Checkboxes on the scene builder, or even make 256 tags on the fxml. And I think that that feature could be useful for many other cases. So if that is possible, I'd love a help on it.

Thanks in advance!


Solution

  • The FXML format does not provide a way to declare elements in a loop. As Scene Builder is simply a WYSIWYG editor for FXML files it also doesn't provide this functionality, nor does it provide a shortcut to declaring and configuring 256 elements (e.g. via a dialog or something). This is one of those cases where adding the nodes in code is the proper solution.

    To do this you'll need to link your FXML file to a controller. From there you can create all 256 CheckBoxes inside the initialize method. This method comes from the Initializable interface, though since JavaFX 8 you no longer need to actually implement the interface. Instead, you just declare a no-arg method named initialize (see this) and annotate it with @FXML if non-public.

    Here's a small example:

    FXML File

    <?xml version="1.0" encoding="UTF-8"?>
    
    <?import javafx.scene.layout.GridPane?>
    <?import javafx.scene.layout.HBox?>
    
    <!-- replace with your root -->
    <HBox xmlns="http://javafx.com/javafx/11.0.2" xmlns:fx="http://javafx.com/fxml/1"
          fx:controller="com.example.Controller">
    
        <GridPane fx:id="grid"/>
    
        <!-- other elements... -->
    
    </HBox>
    

    Controller

    package com.example;
    
    import javafx.fxml.FXML;
    import javafx.scene.control.CheckBox;
    import javafx.scene.layout.GridPane;
    
    public class Controller {
    
        @FXML private GridPane grid;
        private CheckBox[][] boxes; // for access by grid coordinates
    
        @FXML
        private void initialize() {
            boxes = new CheckBox[16][16];
            for (int row = 0; row < boxes.length; row++) {
                for (int col = 0; col < boxes[0].length; col++) {
                    CheckBox box = new CheckBox();
                    grid.add(box, col, row);
                    boxes[row][col] = box;
                }
            }
        }
    
    }