java - 在 JavaFX 中的 TableColumn 的单独线程中生成值

标签 java multithreading javafx

目前在开发我的 JavaFX 应用程序时,我在填充表时遇到了一个小问题。在我当前的设置中,我有以下内容:

    infoColumn.setCellValueFactory(new Callback<TableColumn.CellDataFeatures, ObservableValue>() {
        @Override
        public ObservableValue call(TableColumn.CellDataFeatures cellDataFeatures) {
            DataMessage dataMessage = (DataMessage) cellDataFeatures.getValue();

            SimpleStringProperty infoProperty = new SimpleStringProperty("Loading...");

            if (dataMessage != null) {
                if (isMessageNotification(dataMessage)) {
                    Notification notification = getNotificationFromMessage(dataMessage);
                    infoProperty.set(LanguageUtils.getNotificationInfo(dataMessage, notification));
                } else if (isMessageRequest(dataMessage)) {
                    Request request = getRequestFromMessage(dataMessage);
                    infoProperty.set(LanguageUtils.getRequestInfo(dataMessage, request));
                }
            }

            return infoProperty;
        }
    });

对 LanguageRegistry(我自己的类)的调用使用一些资源来加载与指定对象相关的不同对象。这会导致我的应用程序卡住几秒钟以填充列表,因为这些是它们将实时发送的通知和请求,因此需要在后台加载,这样就不会打扰用户。

我最初尝试的是在另一个线程中运行“if(dataMessage != null)”中的代码,并在代码执行完成时设置 infoProperty 字符串的值。不幸的是,这似乎不起作用,表格只是无限期地显示“正在加载...”。

所以本质上我的问题涉及标题,我需要我的 cellValueFactory 代码在单独的线程中运行,以免卡住应用程序。如果问题中有不清楚的地方,请告诉我,我会更改它。

最佳答案

您不能使用单元格值工厂来延迟加载数据。您可以在模型类 (DataMessage) 本身中执行此操作,如下例所示:

import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.function.Function;
import java.util.stream.IntStream;

import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.ObjectPropertyBase;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class BackgroundLoadingTableCell extends Application {

    private static final Random rng = new Random();

    private static final Executor exec = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), r -> {
        Thread t = new Thread(r);
        t.setDaemon(true);
        return t ;
    });

    @Override
    public void start(Stage primaryStage) {

        TableView<Item> table = new TableView<>();
        table.getColumns().add(column("Item", Item::nameProperty));
        table.getColumns().add(column("Value", Item::valueProperty));
        table.getColumns().add(column("Data", Item::dataProperty));

        IntStream.rangeClosed(1, 100).mapToObj(i -> new Item("Item "+i, rng.nextInt(100))).forEach(table.getItems()::add);

        primaryStage.setScene(new Scene(new BorderPane(table), 600, 600));
        primaryStage.show();
    }

    private <S,T> TableColumn<S,T> column(String text, Function<S, ObservableValue<T>> property) {
        TableColumn<S,T> col = new TableColumn<>(text);
        col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
        return col ;
    }


    public static class Item {
        private final StringProperty name = new SimpleStringProperty();
        private final IntegerProperty value = new SimpleIntegerProperty();

        private final ObjectProperty<String> data = new ObjectPropertyBase<String>() {

            @Override
            public Object getBean() {
                return Item.this;
            }

            @Override
            public String getName() {
                return "data";
            }

            @Override
            public String get() {
                String value = super.get();
                if (value == null) {
                    Task<String> loadDataTask = new Task<String>() {
                        @Override
                        public String call() {
                            return getData(Item.this.getValue());
                        }
                    };
                    loadDataTask.setOnSucceeded(e -> set(loadDataTask.getValue()));
                    exec.execute(loadDataTask);
                    return "Loading..." ;
                }
                return value ;
            }

        };

        public Item(String name, int value) {
            setName(name);
            setValue(value);
        }

        private String getData(int value) {
            // simulate long running process:
            try {
                Thread.sleep(250 + rng.nextInt(500));
            } catch (InterruptedException exc) {
                Thread.currentThread().interrupt();
            }
            return "Data for "+value ;
        }

        public final StringProperty nameProperty() {
            return this.name;
        }


        public final java.lang.String getName() {
            return this.nameProperty().get();
        }


        public final void setName(final java.lang.String name) {
            this.nameProperty().set(name);
        }


        public final IntegerProperty valueProperty() {
            return this.value;
        }


        public final int getValue() {
            return this.valueProperty().get();
        }


        public final void setValue(final int value) {
            this.valueProperty().set(value);
        }

        public final ObjectProperty<String> dataProperty() {
            return this.data;
        }


        public final java.lang.String getData() {
            return this.dataProperty().get();
        }


        public final void setData(final java.lang.String data) {
            this.dataProperty().set(data);
        }

    }

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

或者您可以将DataMessage视为该单元格的值,并在cellFactory中延迟更新该单元格:

import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.function.Function;
import java.util.stream.IntStream;

import javafx.application.Application;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class BackgroundLoadingTableCell extends Application {

    private static final Random rng = new Random();

    private static final Executor exec = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors(), r -> {
        Thread t = new Thread(r);
        t.setDaemon(true);
        return t ;
    });

    @Override
    public void start(Stage primaryStage) {

        TableView<Item> table = new TableView<>();
        table.getColumns().add(column("Item", Item::nameProperty));
        table.getColumns().add(column("Value", Item::valueProperty));
        TableColumn<Item, Item> dataColumn = column("Data", item -> new ReadOnlyObjectWrapper<Item>(item));

        dataColumn.setCellFactory(col -> new TableCell<Item, Item>() {
            private Task<String> dataLoadTask ;

            @Override
            public void updateItem(Item item, boolean empty) {
                super.updateItem(item, empty);
                if (empty) {
                    setText("");
                } else {
                    setText("Loading...");
                    if (dataLoadTask != null) {
                        dataLoadTask.cancel();
                    }
                    dataLoadTask = new Task<String>() {
                        @Override
                        public String call() {
                            return getData(item.getValue());
                        };
                    };
                    dataLoadTask.setOnSucceeded(e -> setText(dataLoadTask.getValue()));
                    exec.execute(dataLoadTask);
                }
            }
        });
        table.getColumns().add(dataColumn);

        IntStream.rangeClosed(1, 100).mapToObj(i -> new Item("Item "+i, rng.nextInt(100))).forEach(table.getItems()::add);

        primaryStage.setScene(new Scene(new BorderPane(table), 600, 600));
        primaryStage.show();
    }

    private String getData(int value) {
        // simulate long running process:
        try {
            Thread.sleep(250 + rng.nextInt(500));
        } catch (InterruptedException exc) {
            Thread.currentThread().interrupt();
        }
        return "Data for "+value ;
    }

    private <S,T> TableColumn<S,T> column(String text, Function<S, ObservableValue<T>> property) {
        TableColumn<S,T> col = new TableColumn<>(text);
        col.setCellValueFactory(cellData -> property.apply(cellData.getValue()));
        return col ;
    }


    public static class Item {
        private final StringProperty name = new SimpleStringProperty();
        private final IntegerProperty value = new SimpleIntegerProperty();

        public Item(String name, int value) {
            setName(name);
            setValue(value);
        }


        public final StringProperty nameProperty() {
            return this.name;
        }


        public final java.lang.String getName() {
            return this.nameProperty().get();
        }


        public final void setName(final java.lang.String name) {
            this.nameProperty().set(name);
        }


        public final IntegerProperty valueProperty() {
            return this.value;
        }


        public final int getValue() {
            return this.valueProperty().get();
        }


        public final void setValue(final int value) {
            this.valueProperty().set(value);
        }

    }

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

关于java - 在 JavaFX 中的 TableColumn 的单独线程中生成值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33706879/

相关文章:

java - 从类路径外的目录加载资源时出现 NPE

java - 处理 controlP5 使用过多内存

java - 本地关闭错误意味着使用 jetty http 客户端发送 https 调用是什么意思?

java - 寻找带有仿函数的并发映射

c# - 以编程方式设置数据库连接优先级

java - 多个请求的阻塞队列行为

javafx - 如何告诉 IntelliJ 从 FXML Loader 获取 FXML Controller 以进行语法突出显示?

java - 如何使用套接字制作JavaFX Chat应用程序?

JAVA:如何调用Object的子类中的方法?

java - 如何找出固件中嵌入的 GZIP 部分的大小?