Reputation: 3655
For some reason, when my TreeTableView is populated with many elements, the icons set on the files will randomly turn completely transparent.
Upon scrolling down, they will refresh.
I've already checked this thread: JavaFX TreeTableView rows gone when scrolling
I assume since I'm on
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)
This version I updated to literally 2-3 days ago after formatting, so I can't see the above version being 2+ years out of date (as the other thread I linked is that old [from 2013]).
A lot of the nodes are buried in other nodes that are not expanded, therefore I'm confused at why clicking on one of the nodes to expand them causes the GUI to chug slowly. When I click on one of the nodes, approximately 15 new elements appear. I don't have an old computer (2.8 Ghz 4 core 8 gig ram), unless such a thing is that taxing?
This is also happening to nodes that are already loaded, so scrolling up and back down will cause them to disappear.
Here is a small example, sometimes it can get ridiculous (I've had up to half of the images on a full screen go blank).
Is this a bug? Or am I doing something wrong? Was the TreeTableView designed to handle this many entries (~50k, but they are nested in each other so only at most 2000-3000 are showing at any given time)?
I have never had this happen when dealing with 100-500 entries, but people who will be using this application will likely be dealing with data that can grow to be much bigger (5000-10000 nested elements which likely will not be showing all at the same time).
The only workaround I can think of is viewing each node by itself, whereby I remove every other node from the table and only display the current one, but this would require a fair amount of reworking which I'd like to avoid if possible.
EDIT: As a note, I use multiple ImageView's for each, so it's not like the problems where someone tries to use one Node in their graph.
=====================================================================
Updated 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);
}
}
Controller:
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 file:
<?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>
This is what happens when I pull the scroller down eventually (usually within 3-4 seconds of dragging it up and down):
Furthermore, the files randomly appear to rip instantly to the left when dragging down. INSTRUCTIONS:
Run the file
Expand the root node
Drag the scrollbar up and down, from top to bottom to make it happen quickly (at least it does for me)
Upvotes: 2
Views: 1916
Reputation: 1135
The suggestion by @jewelsea works with a single icon, but causes problems when more than one icon is required.
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 is reusing the TreeTableCells, which means that the ImageView needs to be updated each time.
Upvotes: 0
Reputation: 159291
Suggested alternate approach
I suggest that, for your use-case, that you don't set a graphic on each TreeItem. Instead, set a graphic in the cell value factory for your tree cells.
When you set a graphic on each TreeItem, you create 50K graphic nodes. When you set a graphic in the cell factory, you only create a single graphic node for each of the visible cells (e.g. 11 graphic nodes for your sample).
Sample code
The code below does not exhibit the behavior your describe in your question and works fine for me:
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);
}
}
Even though the code in this answer will probably work for you, I still encourage you to log a report in the JavaFX issue tracker to request a developer to look into the performance of the original code you posted in your question (in case there is something internal which could be done in the platform code to improve or document its scalability with lots of tree item graphics set).
Upvotes: 4