JavaFX 图标在滚动时从 TreeTableView 中随机消失,性能也很慢
JavaFX icons randomly disappear from TreeTableView when scrolling, performance slow as well
由于某些原因,当我的 TreeTableView 填充了很多元素时,文件上设置的图标会随机变成完全透明。
向下滚动时,它们会刷新。
我已经检查过这个线程:JavaFX TreeTableView rows gone when scrolling
我假设因为我在
java version "1.8.0_45"
Java(TM) SE Runtime Environment (build 1.8.0_45-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)
我在格式化后 2-3 天前更新到这个版本,所以我看不到上面的版本已经过时 2 年以上(因为我链接的另一个线程是那个旧版本 [从 2013 年开始]) .
很多节点都隐藏在其他未展开的节点中,因此我很困惑为什么单击其中一个节点展开它们会导致 GUI 缓慢启动。当我单击其中一个节点时,会出现大约 15 个新元素。我没有旧电脑(2.8 Ghz 4 核 8 gig ram),除非这样的东西很费力?
这也发生在已经加载的节点上,因此上下滚动会导致它们消失。
这是一个小例子,有时它会变得很荒谬(我在全屏上有多达一半的图像是空白的)。
这是一个错误吗?还是我做错了什么? TreeTableView 是否设计用于处理如此多的条目(~50k,但它们相互嵌套,因此在任何给定时间最多只显示 2000-3000 个条目)?
我在处理 100-500 个条目时从未发生过这种情况,但是使用此应用程序的人可能会处理可以增长到更大的数据(5000-10000 个嵌套元素,这些元素可能 不是同时显示所有内容)。
我能想到的唯一解决方法是单独查看每个节点,由此我从 table 中删除所有其他节点并仅显示当前节点,但这需要大量返工,我如果可能,我想避免。
编辑:请注意,我为每个图像视图使用多个 ImageView,因此这不像有人试图在他们的图中使用一个节点的问题。
============================================= ========================
已更新 MCVE:
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
try {
FXMLLoader fxmlloader = new FXMLLoader(Main.class.getResource("Test.fxml"));
AnchorPane root = fxmlloader.load();
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
控制器:
package application;
import javafx.fxml.FXML;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
public class Controller {
@FXML
public AnchorPane rootPane;
@FXML
public TreeTableView<String> ttv;
@FXML
public TreeTableColumn<String, String> ttc;
@FXML
public void initialize() {
Image img = new Image("http://i.imgur.com/TEgfhOw.png");
TreeItem<String> root = new TreeItem<>("Root item");
TreeItem<String> ti;
for (int a = 0; a < 50000; a++) {
ti = new TreeItem<>(Integer.toString(a));
ti.setGraphic(new ImageView(img));
root.getChildren().add(ti);
}
ttv.setRoot(root);
}
}
FXML 文件:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane fx:id="rootPane" prefHeight="400.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Controller">
<children>
<TreeTableView fx:id="ttv" prefHeight="300.0" prefWidth="300.0">
<columns>
<TreeTableColumn fx:id="ttc" prefWidth="195.0" text="C1" />
</columns>
</TreeTableView>
</children>
</AnchorPane>
这是我最终将滚动条下拉时发生的情况(通常在上下拖动它的 3-4 秒内):
此外,当向下拖动时,文件会随机出现并立即撕裂到左侧。
说明:
运行 文件
展开根节点
上下拖动滚动条,从上到下快速移动(至少对我来说是这样)
建议的替代方法
我建议,对于您的用例,您不要在每个 TreeItem 上设置图形。相反,在树单元格的单元格值工厂中设置一个图形。
当您在每个 TreeItem 上设置一个图形时,您创建了 50K 个图形节点。当您在单元工厂中设置图形时,您只需为每个可见单元创建一个图形节点(例如,您的样本有 11 个图形节点)。
示例代码
下面的代码没有表现出您在问题中描述的行为,对我来说工作正常:
package application;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.layout.AnchorPane;
import javafx.util.Callback;
public class Controller {
@FXML
public AnchorPane rootPane;
@FXML
public TreeTableView<String> ttv;
@FXML
public TreeTableColumn<String, String> ttc;
@FXML
public void initialize() {
Image img = new Image("http://i.imgur.com/TEgfhOw.png");
TreeItem<String> root = new TreeItem<>("Root item");
TreeItem<String> ti;
for (int a = 0; a < 50000; a++) {
ti = new TreeItem<>(Integer.toString(a));
// ti.setGraphic(new ImageView(img));
root.getChildren().add(ti);
}
ttc.setCellValueFactory(param ->
new ReadOnlyObjectWrapper<>(param.getValue().getValue())
);
ttc.setCellFactory(new Callback<TreeTableColumn<String, String>, TreeTableCell<String, String>>() {
@Override
public TreeTableCell<String, String> call(TreeTableColumn<String, String> param) {
return new TreeTableCell<String, String>() {
private ImageView imageView;
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty && item != null) {
if (imageView == null) {
imageView = new ImageView(img);
}
setText(item);
setGraphic(imageView);
} else {
setText(null);
setGraphic(null);
}
}
};
}
});
ttv.setRoot(root);
}
}
即使此答案中的代码可能对您有用,我仍然鼓励您在 JavaFX issue tracker 中记录一份报告,请求开发人员检查您在问题(如果有一些内部的东西可以在平台代码中完成,以提高或记录它的可扩展性,有很多树项目图形集)。
@jewelsea 的建议适用于单个图标,但当需要多个图标时会出现问题。
Image pink = new Image (getClass ().getResourceAsStream ("Letter-A-pink-icon.png"));
Image blue = new Image (getClass ().getResourceAsStream ("Letter-A-blue-icon.png"));
TreeItem<String> root = new TreeItem<> ("0");
for (int i = 1; i <= 5000; i++)
{
TreeItem<String> ti = new TreeItem<> (Integer.toString (i));
root.getChildren ().add (ti);
}
ttc.setCellValueFactory (
param -> new ReadOnlyObjectWrapper<> (param.getValue ().getValue ()));
ttc.setCellFactory (
new Callback<TreeTableColumn<String, String>, TreeTableCell<String, String>> ()
{
@Override
public TreeTableCell<String, String>
call (TreeTableColumn<String, String> param)
{
return new TreeTableCell<String, String> ()
{
ImageView imageView;
@Override
protected void updateItem (String item, boolean empty)
{
super.updateItem (item, empty);
if (!empty && item != null)
{
if (imageView == null)
imageView = new ImageView ();
int val = Integer.parseInt (item);
imageView.setImage (val % 2 == 0 ? pink : blue);
setText (item);
setGraphic (imageView);
}
else
{
setText (null);
setGraphic (null);
}
}
};
}
});
ttv.setRoot (root);
}
Java是复用了TreeTableCells,也就是说ImageView每次都需要更新
由于某些原因,当我的 TreeTableView 填充了很多元素时,文件上设置的图标会随机变成完全透明。
向下滚动时,它们会刷新。
我已经检查过这个线程:JavaFX TreeTableView rows gone when scrolling
我假设因为我在
java version "1.8.0_45"
Java(TM) SE Runtime Environment (build 1.8.0_45-b15)
Java HotSpot(TM) 64-Bit Server VM (build 25.45-b02, mixed mode)
我在格式化后 2-3 天前更新到这个版本,所以我看不到上面的版本已经过时 2 年以上(因为我链接的另一个线程是那个旧版本 [从 2013 年开始]) .
很多节点都隐藏在其他未展开的节点中,因此我很困惑为什么单击其中一个节点展开它们会导致 GUI 缓慢启动。当我单击其中一个节点时,会出现大约 15 个新元素。我没有旧电脑(2.8 Ghz 4 核 8 gig ram),除非这样的东西很费力?
这也发生在已经加载的节点上,因此上下滚动会导致它们消失。
这是一个小例子,有时它会变得很荒谬(我在全屏上有多达一半的图像是空白的)。
这是一个错误吗?还是我做错了什么? TreeTableView 是否设计用于处理如此多的条目(~50k,但它们相互嵌套,因此在任何给定时间最多只显示 2000-3000 个条目)?
我在处理 100-500 个条目时从未发生过这种情况,但是使用此应用程序的人可能会处理可以增长到更大的数据(5000-10000 个嵌套元素,这些元素可能 不是同时显示所有内容)。
我能想到的唯一解决方法是单独查看每个节点,由此我从 table 中删除所有其他节点并仅显示当前节点,但这需要大量返工,我如果可能,我想避免。
编辑:请注意,我为每个图像视图使用多个 ImageView,因此这不像有人试图在他们的图中使用一个节点的问题。
============================================= ========================
已更新 MCVE:
package application;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;
public class Main extends Application {
@Override
public void start(Stage primaryStage) {
try {
FXMLLoader fxmlloader = new FXMLLoader(Main.class.getResource("Test.fxml"));
AnchorPane root = fxmlloader.load();
Scene scene = new Scene(root, 400, 400);
primaryStage.setScene(scene);
primaryStage.show();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
launch(args);
}
}
控制器:
package application;
import javafx.fxml.FXML;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeTableColumn;
import javafx.scene.control.TreeTableView;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.AnchorPane;
public class Controller {
@FXML
public AnchorPane rootPane;
@FXML
public TreeTableView<String> ttv;
@FXML
public TreeTableColumn<String, String> ttc;
@FXML
public void initialize() {
Image img = new Image("http://i.imgur.com/TEgfhOw.png");
TreeItem<String> root = new TreeItem<>("Root item");
TreeItem<String> ti;
for (int a = 0; a < 50000; a++) {
ti = new TreeItem<>(Integer.toString(a));
ti.setGraphic(new ImageView(img));
root.getChildren().add(ti);
}
ttv.setRoot(root);
}
}
FXML 文件:
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.control.*?>
<?import java.lang.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<AnchorPane fx:id="rootPane" prefHeight="400.0" prefWidth="400.0" xmlns="http://javafx.com/javafx/8.0.40" xmlns:fx="http://javafx.com/fxml/1" fx:controller="application.Controller">
<children>
<TreeTableView fx:id="ttv" prefHeight="300.0" prefWidth="300.0">
<columns>
<TreeTableColumn fx:id="ttc" prefWidth="195.0" text="C1" />
</columns>
</TreeTableView>
</children>
</AnchorPane>
这是我最终将滚动条下拉时发生的情况(通常在上下拖动它的 3-4 秒内):
此外,当向下拖动时,文件会随机出现并立即撕裂到左侧。 说明:
运行 文件
展开根节点
上下拖动滚动条,从上到下快速移动(至少对我来说是这样)
建议的替代方法
我建议,对于您的用例,您不要在每个 TreeItem 上设置图形。相反,在树单元格的单元格值工厂中设置一个图形。
当您在每个 TreeItem 上设置一个图形时,您创建了 50K 个图形节点。当您在单元工厂中设置图形时,您只需为每个可见单元创建一个图形节点(例如,您的样本有 11 个图形节点)。
示例代码
下面的代码没有表现出您在问题中描述的行为,对我来说工作正常:
package application;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.fxml.FXML;
import javafx.scene.control.*;
import javafx.scene.image.*;
import javafx.scene.layout.AnchorPane;
import javafx.util.Callback;
public class Controller {
@FXML
public AnchorPane rootPane;
@FXML
public TreeTableView<String> ttv;
@FXML
public TreeTableColumn<String, String> ttc;
@FXML
public void initialize() {
Image img = new Image("http://i.imgur.com/TEgfhOw.png");
TreeItem<String> root = new TreeItem<>("Root item");
TreeItem<String> ti;
for (int a = 0; a < 50000; a++) {
ti = new TreeItem<>(Integer.toString(a));
// ti.setGraphic(new ImageView(img));
root.getChildren().add(ti);
}
ttc.setCellValueFactory(param ->
new ReadOnlyObjectWrapper<>(param.getValue().getValue())
);
ttc.setCellFactory(new Callback<TreeTableColumn<String, String>, TreeTableCell<String, String>>() {
@Override
public TreeTableCell<String, String> call(TreeTableColumn<String, String> param) {
return new TreeTableCell<String, String>() {
private ImageView imageView;
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
if (!empty && item != null) {
if (imageView == null) {
imageView = new ImageView(img);
}
setText(item);
setGraphic(imageView);
} else {
setText(null);
setGraphic(null);
}
}
};
}
});
ttv.setRoot(root);
}
}
即使此答案中的代码可能对您有用,我仍然鼓励您在 JavaFX issue tracker 中记录一份报告,请求开发人员检查您在问题(如果有一些内部的东西可以在平台代码中完成,以提高或记录它的可扩展性,有很多树项目图形集)。
@jewelsea 的建议适用于单个图标,但当需要多个图标时会出现问题。
Image pink = new Image (getClass ().getResourceAsStream ("Letter-A-pink-icon.png"));
Image blue = new Image (getClass ().getResourceAsStream ("Letter-A-blue-icon.png"));
TreeItem<String> root = new TreeItem<> ("0");
for (int i = 1; i <= 5000; i++)
{
TreeItem<String> ti = new TreeItem<> (Integer.toString (i));
root.getChildren ().add (ti);
}
ttc.setCellValueFactory (
param -> new ReadOnlyObjectWrapper<> (param.getValue ().getValue ()));
ttc.setCellFactory (
new Callback<TreeTableColumn<String, String>, TreeTableCell<String, String>> ()
{
@Override
public TreeTableCell<String, String>
call (TreeTableColumn<String, String> param)
{
return new TreeTableCell<String, String> ()
{
ImageView imageView;
@Override
protected void updateItem (String item, boolean empty)
{
super.updateItem (item, empty);
if (!empty && item != null)
{
if (imageView == null)
imageView = new ImageView ();
int val = Integer.parseInt (item);
imageView.setImage (val % 2 == 0 ? pink : blue);
setText (item);
setGraphic (imageView);
}
else
{
setText (null);
setGraphic (null);
}
}
};
}
});
ttv.setRoot (root);
}
Java是复用了TreeTableCells,也就是说ImageView每次都需要更新