我正在使用场景图(而不是画布)在JavaFX中进行仿真,并且在仅在屏幕上绘制所需内容时遇到了问题。
此模拟中有1000万个以上的节点,但是用户只需要在屏幕上同时查看一小部分这些节点(最多160,000个节点)。我关心的所有节点都是400x400 ImageViews
。
每个节点都是Group
(节点块)的成员,该节点大约容纳40,000个节点,因此一次需要显示四个或更少的“节点块”。对于要显示的这些“节点块”,它们被添加到静态Pane
上,并且该窗格位于根节点中,即Group
。
所以我从第一个父母到最后一个孩子的图表看起来像这样:
根节点Group
\显示Pane
\(很多)节点块Group
\ <= 40,000 ImageViews
由于基于用户输入的显示窗格不断在移动(平移和缩放),并且节点太多,因此应用程序无法以我想要的速度运行。 JavaFX无法同时跟踪1000万个以上的节点很有意义,因此我的解决方案是从显示窗格中删除所有“节点块”。将它们保存在哈希图中,直到需要绘制它们为止。
每个“节点块”的LayoutX
和LayoutY
设置为均匀地分布在显示窗格中的网格中,如下所示:
在此示例中,我将需要抓取并显示“节点块” 7、8、12和13,因为这是用户看到的。
这是手动添加了“节点块” 0的屏幕截图。呈绿色的黄色是放置“节点块” 1、5和6的位置。
我的问题是:由于直到需要时才将“节点块”添加到显示窗格中,因此我无法参考用户所看到的显示窗格中不断变化的部分的布局边界,所以我这样做不知道需要显示哪些“节点块”。
有没有简单的方法可以解决此问题?还是我走错了路? (或两者都有)。
答案 0 :(得分:1)
注意:这不是您确切问题的答案。
我不知道为每个图像创建ImageView的原因是什么。相反,我会为每个nodeChunk绘制一张图像(160000张图像),并为此nodeChunk创建一个ImageView。
在功能上,我看不到这两种方法之间的任何区别(您可以让我知道为什么要使用每个imageview)。但是从性能上来说,这种方法非常快速,流畅。
import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.scene.Group;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.ScrollPane;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.GridPane;
import javafx.stage.Stage;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.security.SecureRandom;
import java.util.HashMap;
import java.util.Map;
/**
* Combining 4,000,000 images
*/
public class ImageLoadingDemo extends Application {
SecureRandom rnd = new SecureRandom();
// I created 5 images of dimension 1 x 1 each with new color
String[] images = {"1.png", "2.png", "3.png", "4.png", "5.png"};
Map<Integer, BufferedImage> buffImages = new HashMap<>();
Map<Integer, Image> fxImages = new HashMap<>();
@Override
public void start(Stage primaryStage) throws IOException {
ScrollPane root = new ScrollPane();
root.setPannable(true);
Scene scene = new Scene(root, 800, 800);
primaryStage.setScene(scene);
primaryStage.show();
root.setContent(approach1()); // Fast & smooth rendering
// Your approach of creating ImageViews
// root.setContent(approach2()); // Very very very very very slow rendering
}
private Node approach1(){
// Creating 5 x 5 nodeChunk grid
GridPane grid = new GridPane();
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
ImageView nodeChunk = new ImageView(SwingFXUtils.toFXImage(createChunk(), null));
grid.add(nodeChunk, i, j);
}
}
return grid;
}
private BufferedImage createChunk() {
// Combining 160000 1px images into a single image of 400 x 400
BufferedImage chunck = new BufferedImage(400, 400, BufferedImage.TYPE_INT_ARGB);
Graphics2D paint;
paint = chunck.createGraphics();
paint.setPaint(Color.WHITE);
paint.fillRect(0, 0, chunck.getWidth(), chunck.getHeight());
paint.setBackground(Color.WHITE);
for (int i = 0; i < 400; i++) {
for (int j = 0; j < 400; j++) {
int index = rnd.nextInt(5); // Picking a random image
BufferedImage buffImage = buffImages.get(index);
if (buffImage == null) {
Image image = new Image(ImageLoadingDemo.class.getResourceAsStream(images[index]));
buffImages.put(index, SwingFXUtils.fromFXImage(image, null));
}
paint.drawImage(buffImage, i, j, null);
}
}
return chunck;
}
private Node approach2(){
GridPane grid = new GridPane();
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 5; j++) {
grid.add(createGroup(), i, j);
}
}
return grid;
}
private Group createGroup() {
GridPane grid = new GridPane();
for (int i = 0; i < 400; i++) {
for (int j = 0; j < 400; j++) {
int index = rnd.nextInt(5);
Image fxImage = fxImages.get(index);
if (fxImage == null) {
fxImage = new Image(ImageLoadingDemo.class.getResourceAsStream(images[index]));
fxImages.put(index, fxImage);
}
grid.add(new ImageView(fxImage), i, j);
}
}
return new Group(grid);
}
public static void main(String[] args) {
Application.launch(args);
}
}
答案 1 :(得分:0)
我最终为每个“节点块”创建一个矩形,将不透明度设置为零,然后检查与用户看到的显示窗格部分是否存在冲突。当与矩形发生碰撞时,会将相应的“节点块”添加到显示窗格中。