Нужна помощь с динамическим размещением элементов на панели в JavaFX

Я только начал осваивать JavaFX.

Разрабатываю приложение, в котором можно создавать различные голосования. У меня есть задача - динамически размещать в панели названия голосований и кнопки, по которым можно будет перейти в другое окно и посмотреть подробную информацию о голосовании.

Насколько я понял технологию, нужно в обработчике события окна авторизации выгрузить данные с БД, затем загрузить FXML файл моего меню, и туда прямо из кода вставить мои кнопки и названия.

Проблема возникла в том, что у меня не получается получить панель, в которую я хочу вставить эти элементы. Правильный ли у меня ход мыслей касательно алгоритма добавления элементов, и где я ошибся?

Контроллер окна авторизации

package sample;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.*;
import javafx.scene.control.*;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import java.io.IOException;
import java.sql.SQLException;
import java.util.Objects;

public class LogInController
{
    @FXML
    private TextField loginField;
    @FXML
    private PasswordField passwordField;
    @FXML
    private Button loginButton;
    @FXML
    private Button signUpButton;
    @FXML
    private Text notCorrectInfo;

    @FXML
    void initialize()
    {
        loginButton.setOnAction(actionEvent -> {
            try {
                if (Database.authUser(loginField.getText(), passwordField.getText()))
                {
                    Stage stage = (Stage) ((Node) actionEvent.getSource()).getScene().getWindow();
                    Parent parent = FXMLLoader.load(Objects.requireNonNull(getClass().getResource("assets/menu.fxml")));
                    // Adding to votesPane elements
                    Scene scene = new Scene(parent);
                    AnchorPane pane = (AnchorPane) scene.lookup("#votesPane"); // Тут не получается получить панель (pane = null)
                    for (Vote vote :
                            Database.loadVotes()) {
                        Label name = new Label(vote.getName());
                        name.setTextFill(Color.BLACK);
                        pane.getChildren().add(name);
                        pane.getChildren().add(new Button("Открыть"));
                    }
                    stage.setScene(scene);
                }
                else
                    notCorrectInfo.setVisible(true);
            } catch (IOException | ClassNotFoundException | SQLException e) {
                e.printStackTrace();
            }
        });
    }
}

FXML-файл меню, куда я хочу динамически вставить элементы

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.text.*?>

<AnchorPane prefHeight="400.0" prefWidth="600.0" styleClass="outerPane" stylesheets="@style.css"
            xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1"
            fx:controller="sample.MenuController">
    <Button fx:id="createVoteButton" layoutX="435.0" layoutY="21.0" mnemonicParsing="false" prefHeight="38.0"
            prefWidth="144.0" styleClass="greenButton" text="Create vote">
        <font>
          <Font name="Segoe UI" size="20.0"/>
        </font>
    </Button>
    <ScrollPane hbarPolicy="NEVER" layoutX="18.0" layoutY="80.0" prefHeight="306.0" prefWidth="565.0"
                styleClass="scrollPane">
        <AnchorPane fx:id="votesPane" minHeight="0.0" minWidth="0.0" prefHeight="306.0" prefWidth="565.0"
                    styleClass="scrollPane">
        </AnchorPane>
    </ScrollPane>
</AnchorPane>


Ответы (1 шт):

Автор решения: Perfect Voyage

Прежде чем мы начнем :

Замечания по коду :

  • ScrollPane отсутствует fx:id

  • Button не нужен fx:id, назначьте ему setOnAction. Имя метода должно соответствовать имени метода в контроллере.

  • Вам не нужен lookup, у вас есть ГЛАВНАЯ ПАНЕЛЬ, прописанная в FXML, для этого fx:id и существует : связать ваш элемент с кодом.

  • Настоятельно рекомендую почитать про MVC в JavaFX

     public class MenuController {
    
     @FXML
     private AnchorPane mainPane;
    
     @FXML
     private Button createVoteButton;
    
     @FXML
     private AnchorPane votesPane;
    
     public void initialize(){
         createVoteButton.setOnAction(e->pushButton());
     }
    
     private void pushButton(){
         System.out.println("Нажатие кнопки!!!!");
         // Можно заменить на setOnAction в SceneBuilder
         generete();
     }
    
     private void generete(){
         // Сделующий цикл генерирует случайное кол-во кнопок
         // Можно вызвать в блоке initialize, т.е загрузится само.
    
         for (int i = 0 ; i < randomDeleteThisTest(); i++){
             int finalI = i;
    
             Platform.runLater(()->{
                 Button button = new Button("Button # = " + finalI);
                 button.setLayoutX(finalI*50);
                 // AnchorPane накладывает кнопки друг на друга без
                 // явного указания setLayoutX
                 mainPane.getChildren().add(button);
             });
    
             // Platform.runLater(()->{ // Ваш код });
             // Эта конструкция позволяет ОТЛОЖИТЬ изменения визуальной информации
             // они не происходят сию минуту
             // Использовать такое, без проверок, не совсем хорошо
             // Но сейчас в качестве примера сойдёт
         }
     }
    
     private static int randomDeleteThisTest(){
         Random r = new Random();
         return r.nextInt(15);
     }}
    

/////////////////////////////// FXML /////////////////////////////////

<AnchorPane prefHeight="400.0" prefWidth="600.0" styleClass="outerPane"
        xmlns="http://javafx.com/javafx/16" xmlns:fx="http://javafx.com/fxml/1"
        fx:controller="sample.MenuController"
        fx:id="mainPane">
<Button fx:id="createVoteButton" layoutX="435.0" layoutY="21.0" mnemonicParsing="false" prefHeight="38.0"
        prefWidth="144.0" styleClass="greenButton" text="Create vote">
    <font>
        <Font name="Segoe UI" size="20.0"/>
    </font>
</Button>
<ScrollPane hbarPolicy="NEVER" layoutX="18.0" layoutY="80.0" prefHeight="306.0" prefWidth="565.0"
            styleClass="scrollPane">
    <AnchorPane fx:id="votesPane" minHeight="0.0" minWidth="0.0" prefHeight="306.0" prefWidth="565.0"
                styleClass="scrollPane">
    </AnchorPane>
</ScrollPane>
→ Ссылка