当前位置: 首页 > 知识库问答 >
问题:

无需调用刷新即可刷新JavaFX TableView()

戈正初
2023-03-14

如果我想在某个任意ObservableValue已更改但基础TableView数据未更改的情况下使TableView刷新(而不调用refresh()方法),那么对提取器回调方法进行自适应是否是一个好的解决方案?

这是一个使用TableView刷新()方法的实现示例。

package com.example.rtv1;

import java.text.DecimalFormat;
import java.text.NumberFormat;
import javafx.application.Application;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;

public class App extends Application {

    /**
     * A data class exemplar.
     */
    public static class Planet {

        private final StringProperty name;
        private final DoubleProperty mass;
        private final DoubleProperty uncertainty;

        public Planet(String name, double mass, double uncertainty) {
            this.name = new SimpleStringProperty(name);
            this.mass = new SimpleDoubleProperty(mass);
            this.uncertainty = new SimpleDoubleProperty(uncertainty);
        }

        public StringProperty getNameProperty() {
            return name;
        }

        public DoubleProperty getMassProperty() {
            return mass;
        }

        public DoubleProperty getUncertaintyProperty() {
            return uncertainty;
        }
    }

    /**
     * Provides a CellFactory and CellValueFactory that use an external
     * (to the TableView) property to control how data is represented.
     */
    public class DerivedCell<S extends Planet> {

        private final ObservableValue<Boolean> watch;
        private final NumberFormat valueFormatter;
        private final NumberFormat percentFormatter;

        private final DoubleProperty value;

        public DerivedCell(ObservableValue<Boolean> watch) {
            this.watch = watch;
            valueFormatter = new DecimalFormat("0.000E0");
            percentFormatter = new DecimalFormat("0.0000");
            value = new SimpleDoubleProperty(Double.NaN);

            // I could put a listener here to invalidate value (perhaps set
            // it to NAN) when the boolean property is toggled, but my concern
            // is that could fire many listeners.  Is that less overhead than
            // calling TableView.refresh()?
        }

        /**
         * Provides a CellFactory that will change formatting based on
         * an external property.
         */
        public Callback<TableColumn<S, Double>, TableCell<S, Double>> forCell() {
            return list -> new TableCell<S, Double>() {
                @Override
                public void updateItem(Double item, boolean empty
                ) {
                    super.updateItem(item, empty);

                    if (empty) {
                        setText(null);
                    } else {
                        setText(watch.getValue()
                                ? percentFormatter.format(item)
                                : valueFormatter.format(item));
                    }
                }
            };
        }

        /**
         * Provides a CellValueFactory that will change the representation of
         * the data based on an external property.
         */
        public Callback<TableColumn.CellDataFeatures<S, Double>, ObservableValue<Double>> getValue() {
            return r -> {
                var u = r.getValue().getUncertaintyProperty().get();

                if (watch.getValue()) {
                    var v = r.getValue().getMassProperty().get();
                    value.setValue(100.0 * u / v);
                } else {
                    value.setValue(u);
                }

                return value.asObject();
            };
        }
    }

    @Override
    public void start(Stage stage) throws Exception {
        var planets = FXCollections.observableArrayList(
                // From: https://en.wikipedia.org/wiki/List_of_Solar_System_objects_by_size
                new Planet("Mercury", 330.11E21, 0.02E21),
                new Planet("Venus", 4867.5E21, 0.2E21),
                new Planet("Earth", 5972.4E21, 0.3E21),
                new Planet("Mars", 641.71E21, 0.03E21),
                new Planet("Jupiter", 1898187E21, 88E21),
                new Planet("Saturn", 568317E21, 13E21),
                new Planet("Uranus", 86813E21, 4E21),
                new Planet("Neptune", 102413E21, 5E21),
                new Planet("Pluto", 13.03E21, 0.03E21)
        );

        var layout = new VBox();

        var toggle = new CheckBox("Uncertainty as %");
        toggle.selectedProperty().setValue(true);

        var table = new TableView<Planet>();
        var nameCol = new TableColumn<Planet, String>("Name");
        var massCol = new TableColumn<Planet, Double>("Mass");
        var uncCol = new TableColumn<Planet, Double>("Uncertainty");

        var derived = new DerivedCell<Planet>(toggle.selectedProperty());

        nameCol.setCellValueFactory(
                r -> r.getValue().getNameProperty());
        massCol.setCellValueFactory(
                r -> r.getValue().getMassProperty().asObject());
        uncCol.setCellValueFactory(derived.getValue());
        uncCol.setCellFactory(derived.forCell());

        table.getColumns().addAll(nameCol, massCol, uncCol);
        table.setItems(planets);

        // Call the maligned refresh() TableView method when the CheckBox
        // changes state.  It would be fantastic if there was a way to
        // call the fireChanged() method in the observable list...
        toggle.selectedProperty().addListener(
                (var ov, var t, var t1) -> {
                    table.refresh();
                });

        layout.getChildren().addAll(toggle, table);

        var scene = new Scene(layout);
        stage.setTitle("Refreshable TableView");
        stage.setScene(scene);
        stage.show();
    }

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

}

当之前有人问过类似的问题时,@kleopatra表示“不,永远不要使用刷新。”

我有一个底层数据没有变化的情况,只是它在TableView中的表示方式。具体来说,我有两列,一个测量值和一个不确定性。用户能够控制测量单位(例如kg)以及不确定性是否应显示在测量单位中或以百分比显示。

单元工厂和单元值工厂使用控制如何表示值和数字格式的属性。当其中一个属性更改时,将启动更改侦听器并刷新TableView。

  1. 通过setViable()切换可见性关闭和打开;
  2. 从所需表示形式的原始对象创建派生的观察列表并使用setItems();
  3. Futz与节点的大小来触发刷新;
  4. 做一个getItems(),清除TableView,做一个setItems();和
  5. 刷新()方法。

我试过#2和#5,显然都很有效。选项1、3和4看起来很混乱。我不喜欢#2,因为它会浪费内存,我个人认为不应该更改底层数据来反映它是如何向用户表示的。

我看了观察列表中的fire Changed()方法,但是,它是受保护的,不是公开的。从哲学上讲,改变的不是数据,而是表示——所以我不喜欢这个解决方案。

我认为这种方法可以用来解决这个问题,但我还没有实现解决方案(这是这个问题的基本原理)。

我可以为不同的表示创建TableClons并更改可见性以反映所需的配置,但这似乎很浪费(但它可能比刷新()更好——我不确定刷新()是否会导致每次调用时创建所有单元格)。

我在想,用这种风格做点什么可能会引发一种更新,但我还没有详细探讨过这一点。

在上述解决方案中,我认为提取器回调是继续的方式。我确实认为我错过了显而易见的解决方案,因此任何见解都是非常感谢的。

共有1个答案

澹台臻
2023-03-14

由于kleopatra提供的见解和James\u D在过去发布的类似示例,我实现了一个不调用TableView的实现。refresh()方法。我在这里提供了一个解决方案,以防有人感兴趣——尽管所有的功劳都归kleopatra和James\u D所有。

/**
 * Demonstrates a TableView that refreshes via the use of a binding for
 * the cell value.
 *
 * This approach will fire for each affected cell, which might be an
 * issue for large tables.
 */
package com.example.rtv3;

import java.text.DecimalFormat;
import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class App extends Application {

    /**
     * A data class exemplar.
     */
    public static class Planet {
        private final StringProperty name;
        private final DoubleProperty mass;
        private final DoubleProperty uncertainty;

        public Planet(String name, double mass, double uncertainty) {
            this.name = new SimpleStringProperty(name);
            this.mass = new SimpleDoubleProperty(mass);
            this.uncertainty = new SimpleDoubleProperty(uncertainty);
        }

        public StringProperty nameProperty() {
            return name;
        }

        public DoubleProperty massProperty() {
            return mass;
        }

        public DoubleProperty uncertaintyProperty() {
            return uncertainty;
        }
    }

    @Override
    public void start(Stage stage) throws Exception {
        var planets = FXCollections.observableArrayList(
                // From: https://en.wikipedia.org/wiki/List_of_Solar_System_objects_by_size
                new Planet("Mercury", 330.11E21, 0.02E21),
                new Planet("Venus", 4867.5E21, 0.2E21),
                new Planet("Earth", 5972.4E21, 0.3E21),
                new Planet("Mars", 641.71E21, 0.03E21),
                new Planet("Jupiter", 1898187E21, 88E21),
                new Planet("Saturn", 568317E21, 13E21),
                new Planet("Uranus", 86813E21, 4E21),
                new Planet("Neptune", 102413E21, 5E21),
                new Planet("Pluto", 13.03E21, 0.03E21)
        );

        var layout = new VBox();

        var toggle = new CheckBox("Uncertainty as %");
        toggle.selectedProperty().setValue(true);

        var table = new TableView<Planet>();
        var nameCol = new TableColumn<Planet, String>("Name");
        var massCol = new TableColumn<Planet, Double>("Mass");
        var uncCol = new TableColumn<Planet, Number>("Uncertainty");

        nameCol.setCellValueFactory(
                r -> r.getValue().nameProperty());
        massCol.setCellValueFactory(
                r -> r.getValue().massProperty().asObject());

        // Implement a CellValueFactory that uses a DoubleBinding to
        // dynamically change the representation of the data based
        // on an external property.
        // NOTE: Even though DoubleBinding has "Double" in the name,
        // it implements ObservableValue<Number> not ObservableValue<Double>,
        // thus the cell type for the column needs to be Number vice Double.
        // NOTE: More complexity can be achieved by extending the
        // DoubleBinding class instead of using the Binding.createDoubleBinding
        // method.  A listener will need to be added to the checkbox selected
        // property that calls the invalidate() method on the binding.
        uncCol.setCellValueFactory(
                (TableColumn.CellDataFeatures<Planet, Number> r) -> {
                    // Instantiate a DoubleBinding that uses a Callable
                    // to change the representation of the uncertainity
                    // and use the selected property from the checkbox
                    // as a dependency (the dependency will cause invalidate
                    // the binding).
                    return Bindings.createDoubleBinding(
                            // The Callable that computes the value
                            () -> {
                        var u = r.getValue().uncertaintyProperty().get();
                        if (toggle.selectedProperty().getValue()) {
                            var v = r.getValue().massProperty().get();
                            return 100.0 * u / v;
                        } else {
                            return u;
                        }
                    },
                            // The dependency that we want to watch
                            toggle.selectedProperty());
                });

        // Setup the formatting of the uncertainty column to change
        // based on the toggle
        var valueFormatter = new DecimalFormat("0.000E0");
        var percentFormatter = new DecimalFormat("0.0000");
        uncCol.setCellFactory(
                v -> {
                    return new TableCell<>() {
                @Override
                public void updateItem(Number item, boolean empty
                ) {
                    super.updateItem(item, empty);

                    if (empty) {
                        setText(null);
                    } else {
                        setText(toggle.selectedProperty().getValue()
                                ? percentFormatter.format(item)
                                : valueFormatter.format(item));
                    }
                }
            };
                });

        table.getColumns().addAll(nameCol, massCol, uncCol);
        table.setItems(planets);

        layout.getChildren().addAll(toggle, table);

        var scene = new Scene(layout);
        stage.setTitle("Refreshable TableView");
        stage.setScene(scene);
        stage.show();
    }

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

}
 类似资料:
  • 问题内容: 如果将新文档索引到Elasticsearch索引,则可在索引操作后1秒钟左右搜索新文档。但是,可以通过调用或对索引进行操作来强制使该文档可立即搜索。这两个操作之间有什么区别- 结果似乎对他们来说是相同的,可以立即搜索文档。 这些操作中的每一项到底是什么? ES文档似乎并未深入解决此问题。 问题答案: 您得到的答案是正确的,但我认为值得详细说明。 刷新有效地调用了Lucene索引读取器上

  • TL;DR使用AngularFirestore和离子/角度。我希望在用户设备(例如表单上的复选框)之间同步数据,而不会导致所有用户的整页刷新。 我是爱奥尼亚/Angular应用程序的快乐用户,该应用程序用于促进我管理的咖啡店的工作。我在应用程序中有一个现有的清单功能。 该应用程序是使用FireStore作为后端实现的,我使用的是AngularFirestore API。 我在咖啡馆里有几款运行该应

  • 本文向大家介绍使用jQuery调用XML实现无刷新即时聊天,包括了使用jQuery调用XML实现无刷新即时聊天的使用技巧和注意事项,需要的朋友参考一下 HTML: Chat.ashx: 以上所述是小编给大家介绍的使用jQuery调用XML实现无刷新即时聊天,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言哦!

  • 2.12 刷新 2.12.1 描述 此接口用于增加内容刷新任务 2.12.2 请求地址 地址: https://api.bokecs.com/cont/add_refresh 2.12.3 请求方式 POST 2.12.4 请求参数 1) 请求入参 Urls 待刷新的链接 2)请求出参 { "code": "", "message": "" } code:接口响应代码。200表示成功。 mess

  • 首先请原谅我的英语不正确。。。 我有一个primeface的组件的问题,我试图刷新一个p: selectOneMenu从一个p:命令按钮,但它不工作(它的工作在另一个xhtml页面,但不是在这里,我不明白为什么...) 首先,我从更新backingbean属性的p:autocomplete中选择一个项目(例如:userChoose)。 然后p:commandButton可以调用他的监听器并将use

  • 问题内容: 我正在使用$ _SESSION变量通过AJAX发送电子邮件(它们需要在不刷新页面的情况下发送),但是$ _SESSION变量不会自动更新,因此当它更改时,我需要刷新页面以更新变量。 是否可以不刷新而更新$ _SESSION变量? 这是我用来发送电子邮件的代码: 因此,基本上,如果$ _SESSION变量更改,则需要此AJAX电子邮件来识别它,而无需刷新。 谢谢你的帮助 问题答案: 当您