当只有某些项目发生更改时,如何防止Javafx Tableview重新加载所有显示的行?

当只有某些项目发生更改时,如何防止Javafx Tableview重新加载所有显示的行?,java,performance,javafx,javafx-tableview,Java,Performance,Javafx,Javafx Tableview,我试图通过不断更新的多个表视图来提高应用程序的性能。每次更新时,可观察列表中只有1项发生更改。重新加载那些tableview的行,并将css样式应用于这些行,会显著降低应用程序的速度 我观察了tableview重新加载它们的行的方式,并意识到所有行都在不断地从零开始重构,而不依赖于对底层observeList的更改类型。更改视口内的1个项目/视口外的1个项目/完全替换列表会导致行构造的确切数量 当只有某些项发生更改时,是否有方法防止Javafx表视图重新加载所有显示的行 其他可能的调整,以提高性

我试图通过不断更新的多个
表视图来提高
应用程序的性能。每次更新时,
可观察列表中只有1项发生更改。重新加载那些
tableview
的行,并将css样式应用于这些行,会显著降低应用程序的速度

我观察了
tableview
重新加载它们的行的方式,并意识到所有行都在不断地从零开始重构,而不依赖于对底层
observeList
的更改类型。更改视口内的1个项目/视口外的1个项目/完全替换列表会导致行构造的确切数量

当只有某些
项发生更改时,是否有方法防止Javafx
表视图重新加载所有显示的

其他可能的调整,以提高性能不断重新加载
表视图
,将不胜感激

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.property.SimpleStringProperty;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import javafx.util.Duration;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

public class FXApplication extends Application {

    private BorderPane border;
    private TableView<Item> tableView;
    private Button addItemToTv = new Button("Add");
    private Button replaceFirst = new Button("Replace first");
    private Button replaceLast = new Button("Replace last");
    private Button fullReplace = new Button("Full replace");

    private int counter = 0;

    protected Map<Integer, TableRow<Item>> rowsByIndex = new HashMap<>();

    @Override
    public void start(Stage primaryStage) {

        constructTv();
        setButtonActions();
        Scene s = constructScene();

        primaryStage.setScene(s);
        primaryStage.show();
    }

    private Scene constructScene() {
        border = new BorderPane();
        border.setCenter(tableView);
        border.setTop(new HBox(addItemToTv, replaceFirst, replaceLast, fullReplace));

        ScrollPane scp = new ScrollPane();
        scp.setFitToHeight(true);
        scp.setFitToWidth(true);
        scp.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);
        scp.setHbarPolicy(ScrollPane.ScrollBarPolicy.AS_NEEDED);
        scp.setContent(border);

        return new Scene(scp);
    }

    private void setButtonActions() {
        addItemToTv.setOnAction(e -> {
            tableView.getItems().add(new Item("bla"));
            Platform.runLater(this::resetCounter);
        });
        replaceFirst.setOnAction(e -> {
            tableView.getItems().set(0, new Item("blub"));
            Platform.runLater(this::resetCounter);
        });
        fullReplace.setOnAction(e -> {
            ArrayList<Item> items = new ArrayList<>(tableView.getItems());
            tableView.getItems().setAll(items);
            Platform.runLater(this::resetCounter);
        });
        replaceLast.setOnAction(e -> {
            tableView.getItems().set(tableView.getItems().size()-1, new Item("blub"));
            Platform.runLater(this::resetCounter);
        });
    }


    private void resetCounter() {
        Timeline tl = new Timeline(new KeyFrame(Duration.millis(500), (ee) -> {
            System.out.println(counter + " row calls");
            counter = 0;
        }));
        tl.play();

    }

    private Node constructTv() {
        tableView = new TableView<>();
        for(int i = 0; i<10; i++){
            TableColumn<Item, String> col = new TableColumn<>("col " + i);
            col.setCellValueFactory(param -> {
                return new SimpleStringProperty(param.getValue().getString());
            });
            tableView.getColumns().add(col);
        }

        for (int i = 0; i < 30 ; i++) {
            tableView.getItems().add(new Item("bla"));
        }

        tableView.setRowFactory(param -> {
            TableRow<Item> row = new TableRow<>();
            row.itemProperty().addListener((observable, oldValue, newValue) -> {
                Platform.runLater(() -> {
                    rowsByIndex.put(row.getIndex(), row);
                    System.err.println("row change " + row.getIndex());
                    counter++;
                });
            });
            return row;
        });

        return tableView;
    }


    public static void main(String[] args) {
        FXApplication.launch(args);
    }

}
class Item {
    private String string;

    public Item(String s) {
        string = s;
    }

    public String getString() {
        return string;
    }

    public void setString(String string) {
        this.string = string;
    }
}
导入javafx.animation.KeyFrame;
导入javafx.animation.Timeline;
导入javafx.application.application;
导入javafx.application.Platform;
导入javafx.beans.property.SimpleStringProperty;
导入javafx.scene.Node;
导入javafx.scene.scene;
导入javafx.scene.control.*;
导入javafx.scene.layout.*;
导入javafx.stage.stage;
导入javafx.util.Duration;
导入java.util.ArrayList;
导入java.util.HashMap;
导入java.util.Map;
公共类FXApplication扩展了应用程序{
私人边界;
私有TableView TableView;
专用按钮addItemToTv=新按钮(“添加”);
private Button replaceFirst=新按钮(“Replace first”);
private Button replaceLast=新按钮(“Replace last”);
专用按钮完全替换=新按钮(“完全替换”);
专用整数计数器=0;
受保护的映射rowsByIndex=newhashmap();
@凌驾
公共无效开始(阶段primaryStage){
constructv();
setButtoActions();
场景s=constructScene();
初级阶段.第二纪(s);
primaryStage.show();
}
私密场景{
border=新边框窗格();
border.setCenter(tableView);
setTop(新的HBox(additemtov、replaceFirst、replaceLast、fullReplace));
ScrollPane scp=新的ScrollPane();
scp.setFitToHeight(正确);
scp.setFitToWidth(真);
scp.setVbarPolicy(ScrollPane.ScrollBarPolicy.AS_所需);
scp.setHbarPolicy(ScrollPane.ScrollBarPolicy.AS_所需);
scp.setContent(边框);
返回新场景(scp);
}
私有void setButtoActions(){
附加设置动作(e->{
tableView.getItems()添加(新项(“bla”);
Platform.runLater(this::resetCounter);
});
replaceFirst.setOnAction(e->{
tableView.getItems().set(0,新项(“blub”);
Platform.runLater(this::resetCounter);
});
fullReplace.setOnAction(e->{
ArrayList items=新的ArrayList(tableView.getItems());
tableView.getItems().setAll(项);
Platform.runLater(this::resetCounter);
});
替换last.setOnAction(e->{
tableView.getItems().set(tableView.getItems().size()-1,新项目(“blub”);
Platform.runLater(this::resetCounter);
});
}
私有无效重置计数器(){
时间线tl=新时间线(新关键帧(持续时间.毫秒(500),(ee)->{
System.out.println(计数器+“行调用”);
计数器=0;
}));
tl.play();
}
私有节点constructv(){
tableView=新的tableView();
对于(int i=0;i{
返回新的SimpleStringProperty(param.getValue().getString());
});
tableView.getColumns().add(col);
}
对于(int i=0;i<30;i++){
tableView.getItems()添加(新项(“bla”);
}
tableView.setRowFactory(参数->{
TableRow行=新TableRow();
row.itemProperty().addListener((可观察、旧值、新值)->{
Platform.runLater(()->{
rowsByIndex.put(row.getIndex(),row);
System.err.println(“行更改”+row.getIndex());
计数器++;
});
});
返回行;
});
返回表视图;
}
公共静态void main(字符串[]args){
FXApplication.launch(args);
}
}
类项目{
私有字符串;
公共项目(字符串s){
字符串=s;
}
公共字符串getString(){
返回字符串;
}
公共无效设置字符串(字符串){
this.string=string;
}
}

使用
平台没有意义。请稍后运行
时间线
<代码>平台.runLater(this::resetCounter)是无用的。此外,每次按下按钮,基本上都在创建新的时间线,并且所有时间线都在运行。这与高性能代码相反(可能不是,但使用了大量不必要的资源)。为什么在cell工厂中使用行侦听器?为什么要在cell工厂稍后运行platform.run。我觉得你做的每件事都与表演的理念背道而驰。您需要从一些基本的JavaFX教程开始。这只是一个虚拟程序,用于显示在对ObservableListener应用更改时调用RowListener的次数,我们知道这是一个示例—但正如@Sedrick已经指出的那样:看起来您所做的一切都是为了人为地减慢一切;)重复:无需在此处使用的任何位置调用Platform.runlater。您是否真的分析了您的应用程序,以及它揭示了哪些瓶颈?好的,我同意Platform.runlater i