为了账号安全,请及时绑定邮箱和手机立即绑定

JavaFX 表行更新

JavaFX 表行更新

撒科打诨 2021-10-13 12:48:05
我试图实现的场景是,每当一个特定TableCell的TableRow被更新,该行的颜色会变为红色,并在3秒后的颜色应自动恢复到原来的。下面是MCVE,主类import java.util.concurrent.ExecutorService;import java.util.concurrent.Executors;import javafx.application.Application;import javafx.collections.FXCollections;import javafx.collections.ObservableList;import javafx.scene.Scene;import javafx.scene.control.TableColumn;import javafx.scene.control.TableView;import javafx.stage.Stage;public class TestProjectWin10 extends Application {    private final ObservableList<Element> data = FXCollections.observableArrayList();    public final Runnable changeValues = () -> {        int i = 0;        while (i <= 100000) {            if (Thread.currentThread().isInterrupted()) {                break;            }            data.get(0).setOccurence(System.currentTimeMillis());            data.get(0).count();            i = i + 1;        }    };    private final ExecutorService executor = Executors.newSingleThreadExecutor(runnable -> {        Thread t = new Thread(runnable);        t.setDaemon(true);        return t;    });    @Override    public void start(Stage primaryStage) {        TableView<Element> table = new TableView<>();        table.getStylesheets().add(this.getClass().getResource("tableColor.css").toExternalForm());        table.setEditable(true);        TableColumn<Element, String> nameCol = new TableColumn<>("Name");        nameCol.setPrefWidth(200);        nameCol.setCellValueFactory(cell -> cell.getValue().nameProperty());        nameCol.setCellFactory((TableColumn<Element, String> param) -> new ColorCounterTableCellRenderer(table));        table.getColumns().add(nameCol);        this.data.add(new Element());    }这是什么问题..?我检查当前时间和更新发生时间之间的差异是否小于 3 秒 - 行颜色为红色(在ColorCounterTableCellRenderer-updateItem方法中)在单独的计时器 ( ColorCounterTableCellRenderer) 中,我尝试检查当前时间和更新发生时间之间的差异是否超过 3 秒 - 去除红色。但是在计时器(createTimer- 方法)代码中:tr.getItem()总是null,因此不会删除红色。这是实现我想要的正确方法吗?为什么tr.getItem()返回null。测试:我运行代码并等待executor代码结束并检查红色是否在 3 秒后被移除。
查看完整描述

2 回答

?
慕姐8265434

TA贡献1813条经验 获得超2个赞

对 UI 的任何更新,即使是通过侦听器触发的,也需要从应用程序线程完成。(您可以通过使用 进行更新来解决此问题Platform.runLater。)


此外,您不能依赖同一个单元格在它应该显示为标记的整个时间内保持相同的单元格。


为了克服这个问题,您需要将有关标记单元格的信息存储在项目本身或一些可观察的外部数据结构中。


以下示例将上次更新的时间存储在 a 中,ObservableMap并使用 aAnimationTimer从地图中清除过期条目。此外,它使用TableRows 根据地图的内容更新伪类。


private static class Item {


    private final IntegerProperty value = new SimpleIntegerProperty();

}


private final ObservableMap<Item, Long> markTimes = FXCollections.observableHashMap();

private AnimationTimer updater;


private void updateValue(Item item, int newValue) {

    int oldValue = item.value.get();

    if (newValue != oldValue) {

        item.value.set(newValue);


        // update time of item being marked

        markTimes.put(item, System.nanoTime());


        // timer for removal of entry

        updater.start();

    }

}


@Override

public void start(Stage primaryStage) {

    Item item = new Item(); // the item that is updated

    TableView<Item> table = new TableView<>();

    table.getItems().add(item);


    // some additional items to make sure scrolling effects can be tested

    IntStream.range(0, 100).mapToObj(i -> new Item()).forEach(table.getItems()::add);


    TableColumn<Item, Number> column = new TableColumn<>();

    column.getStyleClass().add("mark-column");

    column.setCellValueFactory(cd -> cd.getValue().value);

    table.getColumns().add(column);


    final PseudoClass marked = PseudoClass.getPseudoClass("marked");


    table.setRowFactory(tv -> new TableRow<Item>() {


        final InvalidationListener reference = o -> {

            pseudoClassStateChanged(marked, !isEmpty() && markTimes.containsKey(getItem()));

        };

        final WeakInvalidationListener listener = new WeakInvalidationListener(reference);


        @Override

        protected void updateItem(Item item, boolean empty) {

            boolean wasEmpty = isEmpty();

            super.updateItem(item, empty);


            if (empty != wasEmpty) {

                if (empty) {

                    markTimes.removeListener(listener);

                } else {

                    markTimes.addListener(listener);

                }

            }


            reference.invalidated(null);

        }


    });


    Scene scene = new Scene(table);

    scene.getStylesheets().add("style.css");

    primaryStage.setScene(scene);

    primaryStage.show();


    updater = new AnimationTimer() {


        @Override

        public void handle(long now) {

            for (Iterator<Map.Entry<Item, Long>> iter = markTimes.entrySet().iterator(); iter.hasNext();) {

                Map.Entry<Item, Long> entry = iter.next();


                if (now - entry.getValue() > 2_000_000_000L) { // remove after 1 sec

                    iter.remove();

                }

            }


            // pause updates, if there are no entries left

            if (markTimes.isEmpty()) {

                stop();

            }

        }

    };


    final Random random = new Random();


    Thread t = new Thread(() -> {


        while (true) {

            try {

                Thread.sleep(4000);

            } catch (InterruptedException ex) {

                continue;

            }

            Platform.runLater(() -> {

                updateValue(item, random.nextInt(4));

            });

        }

    });

    t.setDaemon(true);

    t.start();

}

样式文件

.table-row-cell:marked .table-cell.mark-column {

    -fx-background-color: red;

}


查看完整回答
反对 回复 2021-10-13
  • 2 回答
  • 0 关注
  • 245 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信