【问题标题】:Dynamically adding nodes in JavaFX在 JavaFX 中动态添加节点
【发布时间】:2021-12-23 06:43:52
【问题描述】:

我正在尝试构建一个在 JavaFX 中实现群聊的聊天应用程序。我想在边框窗格中创建一个滚动窗格,该窗格将包含用户所属的所有组。当用户加入群组图标(ImageViews)时,需要动态添加(不能在场景生成器中完成)到滚动窗格(HBox 内部)。

目前我正在使用一个负责所有舞台和场景更改的 SceneController 类。

package com.ong.Controllers;

import com.ong.Main;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.stage.Stage;

import java.io.IOException;
import java.util.HashMap;

public class ScreenController {
    private static HashMap<String, Scene> screenMap = new HashMap<>();
    private static Stage main;

    public ScreenController(Stage _main){
        main = _main;
    }

    public static void addScreen(String name, FXMLLoader fxmlLoader){
        Scene scene = null;
        try {
            scene = new Scene(fxmlLoader.load());
        } catch (IOException e) {
            e.printStackTrace();
        }
        screenMap.put(name,scene);
    }

    public static void addScene(String name, Scene scene){
        screenMap.put(name,scene);
    }

    public static void removeScreen(String name){
        screenMap.remove(name);
    }

    public static void setMinimumDimensions(int width, int height){
        main.setMinWidth(width);
        main.setMinHeight(height);
    }

    public static void setMaximized(boolean full){
        main.setMaximized(full);
    }

    public static void activate(String name){
        main.setScene(screenMap.get(name));
        main.getIcons().add(new Image(Main.class.getResource("Images/not_logo.png").toString()));
        main.setTitle(name);
        main.show();
    }
}

我已经创建了一个 FXML 文件(使用场景构建器),其中包含边框窗格和滚动窗格作为顶级子项。

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

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

<BorderPane fx:id="borderPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="1080.0" prefWidth="1920.0" xmlns="http://javafx.com/javafx/11.0.2" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.ong.Controllers.HomepageController">
   <top>
      <ScrollPane fx:id="groupsMenuScrollPane" hbarPolicy="NEVER" prefHeight="62.0" prefWidth="1920.0" stylesheets="@../CSS/groups_menu.css" vbarPolicy="NEVER" BorderPane.alignment="CENTER" />
   </top>
</BorderPane>

我的计划是将所有组(初始化时)填充到页面控制器中的滚动窗格中。

package com.ong.Controllers;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.shape.Circle;

import java.net.URL;
import java.util.ResourceBundle;

public class HomepageController implements Initializable {

    @FXML
    private ScrollPane groupsMenuScrollPane;

    @FXML
    private BorderPane borderPane;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        final ImageView imageView = new ImageView("com/ong/Images/not_logo.png");
        final Circle clip = new Circle(300, 200, 200);
        imageView.setClip(clip);

        HBox hbox = new HBox();
        hbox.getChildren().add(imageView);

        groupsMenuScrollPane.setContent(hbox);
        borderPane.setTop(groupsMenuScrollPane);

        Scene scene = new Scene(borderPane);

        ScreenController.addScene("Homepage", scene);
        ScreenController.activate("Homepage");
    }
}

我已经尝试使用更新的边框窗格创建场景,但我收到错误消息“边框窗格已设置为另一个场景的根”。我也尝试直接更改已经存在的场景的根,但在这种情况下我得到了 NullPointerException。

您能否告知将节点动态添加到滚动窗格的最佳方式。我也将感谢 cmets 关于项目结构的信息。

【问题讨论】:

  • 这种结构没有任何意义。 ScreenController 中的 Everythingstatic(这很糟糕,但还可以)。那么为什么它有一个构造函数呢?而且您还没有真正向我们展示问题的原因:只是用“当我尝试使用更新的边框窗格创建场景时”暗示它。你为什么要这样做呢?如果您更新内容,它将在其现有场景中更新。
  • @James_D 嗯好的。谢谢!那么您能否建议更改和跟踪场景的最佳方法是什么?我真的研究了这个主题,恕我直言,这是我找到的最佳解决方案。
  • 我只是不明白你在做什么。您所要做的就是向HBox 添加内容。整个“在不同场景之间切换”的事情似乎与您在这里实际询问的内容完全无关。
  • 我想问题是:你的问题到底是什么?是关于“将内容动态添加到用 FXML 创建的滚动窗格”吗?还是关于“改变和跟踪场景”?这些是完全不同的问题,甚至不应该出现在同一个问题中。

标签: java javafx responsive-design scenebuilder


【解决方案1】:

您能否建议将节点动态添加到滚动窗格的最佳方式。

您需要对ScrollPane(在本例中为HBox)的内容的引用,以及向其中添加内容的方法:

public class HomepageController implements Initializable {

    @FXML
    private ScrollPane groupsMenuScrollPane;

    @FXML
    private BorderPane borderPane;

    private HBox groupContainer ;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        final ImageView imageView = new ImageView("com/ong/Images/not_logo.png");
        final Circle clip = new Circle(300, 200, 200);
        imageView.setClip(clip);

        groupContainer = new HBox();
        groupContainer.getChildren().add(imageView);

        groupsMenuScrollPane.setContent(groupContainer);

        // this is not needed since it's already done in FXML:
        // borderPane.setTop(groupsMenuScrollPane);

        // ...
    }

    public void addGroup(...) {
        // not sure exactly what you want to add, but for example,
        // to add a new image view you would do:
        ImageView imageView = new ImageView(...);
        groupContainer.getChildren().add(imageView);
    }
}

加载 FXML 时,保留对控制器的引用:

FXMLLoader loader = new FXMLLoader(getClass().getResource(...));
Parent root = loader.load();
HomepageController controller = loader.getController();

然后你可以添加东西到滚动窗格中

controller.addGroup(...);

正如另一个答案中提到的,ListView 可能是解决此问题的更好方法。

您还应该考虑一种 MVC 方法(例如,参见Applying MVC With JavaFx),其中模型使用ObservableList&lt;...&gt; 来存储组列表。控制器可以观察该列表并在滚动窗格内容更改时对其进行修改。然后你所要做的就是通过模型向列表中添加一些东西。

【讨论】:

  • 谢谢。这就是我一直在寻找的。​​span>
【解决方案2】:

您的问题和问题是不同的问题。

对于您的问题。动态添加您想要查看 ListView 的元素。 https://docs.oracle.com/javase/8/javafx/api/javafx/scene/control/ListView.html

Anything like recyclerview or reusable views for javafx

至于你的问题。场景只能有一个根节点。在这个根目录中,你应该有你的边界窗格,其中应该是你的 ListView。

【讨论】:

  • ListView 管理自己的滚动。所以如果你使用ListView,就不需要ScrollPane
  • 好收获。我会更新我的答案。
  • 对不起,我没有提到这是一个“问题”。我只是想描述一下我已经尝试过的东西。
  • 不用担心。只是想帮助您更好地描述您要完成的工作。
猜你喜欢
  • 1970-01-01
  • 2017-05-05
  • 2012-10-24
  • 1970-01-01
  • 2019-05-20
  • 1970-01-01
  • 2018-12-04
  • 2010-09-17
相关资源
最近更新 更多