当前位置: 首页 > 知识库问答 >
问题:

Javafx 表视图项可防止重复项

左丘嘉言
2023-03-14

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

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

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

我还计划计算tableview中a列的some,所以我需要

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

我真的对正确的方法感到困惑。所以,我需要你的提示

共有1个答案

嵇弘新
2023-03-14

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

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

此示例将所有这些拼凑在一起。与按钮关联的修改方法都在 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);
    }
}
 类似资料:
  • 我在tableview中使用的一列有问题<本栏最初用于显示我拥有的一种数据类型: 我想更改此列中显示的数据,因此我更改了FXML文件中的列名,并编写了以下内容: 我的ResearchData类编码如下: (我只保留了rang、luse和lSysteme,但我有很多其他变量都是这样写的)。 当我启动程序时,在tableview中,除Systeme列外,所有列都正确显示,我收到了以下错误消息: 我可以

  • 问题内容: 我想创建一个表来存储设备设置。该表具有三行:id,parameter_name和parameter_value。 该表是通过执行以下查询语句创建的: 然后通过执行以下方法存储行: 创建数据库后,将存储默认值: 但是,方法insertRow()的问题在于它无法防止重复输入。 有谁知道在这种情况下如何防止重复输入? 问题答案: 您可以使用列约束。 UNIQUE约束导致在指定列上创建唯一索引

  • 我正在设计一个主要通过键盘控制的Android应用程序。 在此应用程序中,有两个彼此相邻的列表视图。但是,当焦点在列表视图之间更改时,新列表视图中的选择将更改为最接近旧列表视图中所选内容的项目 - 我不希望所选项目更改,直到用户随后在键盘上按 UP 或 DOWN 键。 例子: 列表A未聚焦,选择了项目3。列表B当前具有焦点,并且在该列表视图中,项目B当前被选择。 目前,如果用户按下键盘上的“左”,

  • 我有这个MySQL表 我在这里的目标是防止数据库为给定项目创建重复的。例如,具有的项目具有,但如果我再次输入,它将阻止它输入。但是,如果并且给定的,那么它应该不会有任何问题地插入它。我的问题是,如何防止每个项目重复?

  • 我有一个带有的javafx应用程序。只有第一列()是可编辑的,它包含用于编辑的。现在,完成了它应该做的事情,但是,当我开始编辑单元格,然后在没有提交编辑的情况下将其滚动到视图之外时,会出现一些奇怪的bug。我很肯定这是因为重用单元格的方式。然而,我还没有找到任何方法来干预它,例如禁止重用当前正在编辑的单元格。你能帮我做那个吗。 列的值如下所示: 如果这是混淆的,我为提供的项是(从0到n-1),在不

  • 我想防止重复的值进入数据库表从一个使用PHP的表单。 具有名为clients的表的数据库: 一个名为form的简单表单。html 名为frm_脚本的表单处理脚本。php 到目前为止,我的frm_脚本。php文件,上面的作品和一个独特的记录显示客户添加。然而,对于重复记录,它抛出“错误查询数据库”。 如何更新以下frm_script.php脚本? 如果在输入名/姓组合时发现重复行,则应显示消息“Cl