您可以创建一个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);
}
}