Вы можете создать 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);
}
}
Для первой части вопроса вы можете создать ObservableList, используя 'FXCollections.observableArrayList (set);'. Я не уверен, что именно вы хотите во второй части вашего вопроса. – ItachiUchiha