Javafx tableview items prevent duplicates

六眼飞鱼酱① 提交于 2019-12-12 18:30:09

问题


I'm using javafx tableview with observableList list, I tried to prevent list from holding duplicate items. After doing some search, i figure out that an observableSet can do this job by overidding thoes methodes:equals() and hashcode().

But the problem that javaFX tableview can't hold an observable set:

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

I also planned to calculate the some for a columns in my tableview so, i need

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

I really confused about the right way to do this. So, i need your hints


回答1:


You can create an ObservableSet and then add a listener to it which updates an ObservableList which is used as the items list for the table. As long as modifications are not made directly to the table's items (only to the set, which you can enforce by using an unmodifiable list for the table), then the table will always contain exactly the same items as the set.

To track the total of the values of a property of all the items in the list, you need can register a listener with the list, and recompute the total when it changes. If the property itself may change, you can use an extractor when you create the list, so that the list will fire update notifications if that property changes for any of the list elements.

This example pieces all this together. The modification methods associated with the buttons all operate on the ObservableSet. Notice that if you try to add an item which is equal to an existing item, nothing changes (because adding to the set does nothing, and so no updates are fired to the list).

You can select and modify existing items using the increment and decrement buttons, and you'll see the updates reflected in the total.

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);
    }
}


来源:https://stackoverflow.com/questions/35187145/javafx-tableview-items-prevent-duplicates

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!