3

我正在使用带有 observableList 列表的 javafx tableview,我试图防止列表包含重复项。在做了一些搜索之后,我发现 observableSet 可以通过覆盖方法来完成这项工作:equals() 和 hashcode()。

但是javaFX tableview不能保持可观察集的问题:

tableView.setItems(FXCollections.observableSet(new hashSet<T>());

我还计划为我的表格视图中的列计算一些,所以我需要

  // After change in element T the total will change
  ObservableList<T> listItems = FXCollections.observableArrayList(
                T -> new Observable[]{T.doSomeCalculeProperty});

我真的很困惑正确的方法来做到这一点。所以,我需要你的提示

4

1 回答 1

2

您可以创建一个ObservableSet然后向其添加一个侦听器,该侦听器会更新ObservableList用作表的项目列表的一个侦听器。只要不直接对表的项目进行修改(仅对集合,您可以通过对表使用不可修改的列表来强制执行),那么表将始终包含与集合完全相同的项目。

要跟踪列表中所有项目的属性值的总和,您需要在列表中注册一个侦听器,并在它发生变化时重新计算总和。如果属性本身可能发生变化,您可以在创建列表时使用提取器,以便在任何列表元素的该属性发生更改时,列表将触发更新通知。

这个例子将所有这些拼凑在一起。与按钮相关的修改方法都在ObservableSet. 请注意,如果您尝试添加与现有项目相同的项目,则不会发生任何变化(因为添加到集合中没有任何作用,因此不会对列表触发更新)。

您可以使用增量和减量按钮选择和修改现有项目,您会看到更新反映在总数中。

import java.util.HashSet;
import java.util.Objects;
import java.util.stream.Collectors;

import javafx.application.Application;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.collections.ObservableSet;
import javafx.collections.SetChangeListener.Change;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.control.TextFormatter;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

public class UniqueItemTableViewWithTotal extends Application {

    // creates a table view which always contains the same items as the provided set
    private TableView<Item> createTableView(ObservableSet<Item> items, IntegerProperty total) {

        TableView<Item> table = new TableView<>();
        table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);

        // Want the table's items list to fire updates if the value of any item changes
        // This allows observing the list for tracking the total of all values
        ObservableList<Item> itemList = FXCollections.observableArrayList(
                item -> new Observable[] {item.valueProperty()});

        // register a listener with the set and update the list if the set changes
        // this ensures the list will always contain the same elements as the list,
        items.addListener((Change<? extends Item> c) -> {
            if (c.wasAdded()) {
                itemList.add(c.getElementAdded());
            }
            if (c.wasRemoved()) {
                itemList.remove(c.getElementRemoved());
            }
        });

        // usual column setup
        TableColumn<Item, String> nameCol = new TableColumn<>("Item");
        nameCol.setCellValueFactory(cellData -> cellData.getValue().nameProperty());
        TableColumn<Item, Integer> valueCol = new TableColumn<>("Value");
        valueCol.setCellValueFactory(cellData -> cellData.getValue().valueProperty().asObject());

        table.getColumns().add(nameCol);
        table.getColumns().add(valueCol);

        // use an unmodifiable list for the table to prevent any direct updates to the
        // table's list (updates must go through the set)
        table.setItems(FXCollections.unmodifiableObservableList(itemList));

        // update total if the items list changes:
        itemList.addListener((ListChangeListener.Change<? extends Item> c) -> 
            total.set(itemList.stream()
                    .collect(Collectors.summingInt(Item::getValue))));

        // add any existing elements:
        itemList.addAll(items);

        return table ;
    }


    @Override
    public void start(Stage primaryStage) {
        ObservableSet<Item> items = FXCollections.observableSet(new HashSet<>());
        IntegerProperty total = new SimpleIntegerProperty();

        TableView<Item> table = createTableView(items, total);

        for (int i = 1; i <=5 ; i++) {
            items.add(new Item("Item "+i, 1+(int)(Math.random()*20)));
        }

        // label to display the total of all values:
        Label totalLabel = new Label();
        totalLabel.textProperty().bind(total.asString("Total: %d"));
        totalLabel.setStyle("-fx-font-size:24; -fx-padding:10;");

        // text fields for new item:
        TextField itemField = new TextField();
        TextField valueField = new TextField();

        // restrict value field to valid integers:
        valueField.setTextFormatter(new TextFormatter<Integer>(c -> 
                c.getControlNewText().matches("-?\\d*") ? c : null));

        // button to add new item:
        Button addButton = new Button("Add");
        addButton.setOnAction(e -> {
            Item item = new Item(itemField.getText(), Integer.parseInt(valueField.getText()));
            items.add(item);
            itemField.clear();
            valueField.clear();
        });
        addButton.disableProperty().bind(itemField.textProperty().isEmpty()
                .or(valueField.textProperty().isEmpty()));

        ObservableList<Item> selection = table.getSelectionModel().getSelectedItems();

        // button to remove selected item(s):
        Button removeButton = new Button("Delete");
        removeButton.setOnAction(e ->
                items.removeIf(new HashSet<Item>(selection)::contains));
        removeButton.disableProperty().bind(Bindings.isEmpty(selection));

        // button to increment selected item(s):
        Button incButton = new Button("Increment");

        incButton.setOnAction(e -> selection.forEach(Item::increment));
        incButton.disableProperty().bind(Bindings.isEmpty(selection));

        // button to decrement selected item(s):
        Button decButton = new Button("Decrement");
        decButton.setOnAction(e -> selection.forEach(Item::decrement));
        decButton.disableProperty().bind(Bindings.isEmpty(selection));

        HBox controls = new HBox(5, itemField, valueField, addButton, removeButton, incButton, decButton);
        controls.setAlignment(Pos.CENTER);
        controls.setPadding(new Insets(5));

        BorderPane root = new BorderPane(table);
        root.setTop(totalLabel);
        root.setBottom(controls);

        Scene scene = new Scene(root, 800, 800);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    // model item:
    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 String getName() {
            return this.nameProperty().get();
        }


        public final void setName(final 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 void increment() {
            setValue(getValue()+1);
        }

        public void decrement() {
            setValue(getValue()-1);
        }

        @Override
        public int hashCode() {
            return Objects.hash(getName(), getValue());
        }

        @Override
        public boolean equals(Object o) {
            if (o.getClass() != Item.class) {
                return false ;
            }
            Item other = (Item) o ;
            return Objects.equals(getName(), other.getName())
                    && getValue() == other.getValue() ;
        }
    }

    public static void main(String[] args) {
        launch(args);
    }
}
于 2016-02-04T01:35:32.257 回答