Javafx tableview项目可防止重复

时间:2016-02-03 20:33:16

标签: arraylist tableview javafx-8 hashset observablelist

我使用javafx tableview和observableList列表,我试图阻止列表保存重复项。 在做了一些搜索后,我发现observableSet可以通过重写方法来做这个工作:equals()和hashcode()。

但javaFX tableview无法保存可观察集的问题:

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

我还计划在tableview中计算一些列,所以我需要

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

我真的很担心这样做的正确方法。所以,我需要你的提示

1 个答案:

答案 0 :(得分: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); } } 上运行。请注意,如果您尝试添加一个等于现有项目的项目,则不会发生任何更改(因为添加到该集合不会执行任何操作,因此不会向列表触发更新)。

您可以使用递增和递减按钮选择和修改现有项目,并且您将看到总计中反映的更新。

locs = locals()
for arg in inspect.getargspec(self.__init__)[0][1:]:
    setattr(self, arg, locs[arg])