添加列表项时,不刷新JavaFX TableView行

时间:2018-06-07 18:01:47

标签: java javafx javafx-8

我有两个控制器,一个显示部件列表的主屏幕和一个用于打开第二个控制器窗口以添加新部件的按钮。

当我添加一个部件时,它会添加到arraylist中,但是tableview不会更新。我在顶部有一个搜索字段,如果我点击按钮它会显示新项目,但我尝试的任何内容都不会让表格刷新并在添加新项目时显示。

这是MainController

@Override
public void initialize(URL url, ResourceBundle rb) {
    if (firstLoad) {
        inventory.addPart(new Part(1,"Widget",1.13,5,1,8));
        inventory.addPart(new Part(2,"Sprocket",2.88,5,1,8));
        inventory.addPart(new Part(3,"Gear",3.46,5,1,8));

        firstLoad = false;
    }

    colPartID.setCellValueFactory(new PropertyValueFactory<>("partID"));
    colPartName.setCellValueFactory(new PropertyValueFactory<>("name"));
    colPartInvLevel.setCellValueFactory(new PropertyValueFactory<>("inStock"));
    colPartPrice.setCellValueFactory(new PropertyValueFactory<>("price"));

    tblParts.setItems(FXCollections.observableArrayList(inventory.getAllParts()));

}

库存类具有零件的静态arraylist

private static ArrayList<Part> allParts = new ArrayList<>();

并且addpartcontroller只是添加到arraylist,它可以正常工作

inv.addPart(new Part(1,"test",2,3.46));
stage.close();

舞台关闭后,主屏幕似乎根本没有更新,零件表视图中仍有3个部分

如果我将搜索文本字段留空并点击搜索按钮,则第4部分显示

        FilteredList<Part> filteredData = new FilteredList<>(FXCollections.observableArrayList(inventory.getAllParts()), p -> true);

        String newValue = txtPartSearch.getText();

        filteredData.setPredicate(part -> {
            if (newValue == null || newValue.isEmpty()) {
                return true;
            }

            String lowerCaseFilter = newValue.toLowerCase();
            if (part.getName().toLowerCase().contains(lowerCaseFilter)) {
                return true;
            }
            return Integer.toString(part.getPartID()).equals(lowerCaseFilter);
        });

        SortedList<Part> sortedData = new SortedList<>(filteredData);
        sortedData.comparatorProperty().bind(tblParts.comparatorProperty());
        tblParts.setItems(sortedData);

我试过tblParts.refresh(),我也试过让addpartcontroller在maincontroller中调用一个方法在关闭之前设置表,但除非我调用搜索方法,否则表永远不会刷新。

编辑:

如果在maincontroller中完成所有操作,一切正常。例如,如果我删除上面的整个搜索代码并将其替换为以下两行(按下时在主控制器上执行),则主控制器上的表会更新以立即显示新项目。

inventory.addPart(new Part(6,"Test",5.23,4,2,8));
tblParts.setItems(FXCollections.observableArrayList(inventory.getAllParts()));

1 个答案:

答案 0 :(得分:1)

您的代码存在一些问题。与您使用FXCollections.observableArrayList(Collection)最相关的是。这种方法的Javadoc说明:

  

创建一个新的可观察数组列表,并向其添加集合col的内容。

换句话说,Collection复制到由ObservableList支持的新ArrayList。原始Collection的所有更新都不会添加到ObservableList。如果您希望传递的FXCollections.observableList(List)成为支持List,则应使用List

FXCollections.observableList(List)的Javadoc(强调我的):

  

构造由指定列表支持的ObservableList。 ObservableList实例上的变异操作将报告给已在该实例上注册的观察者。   请注意,直接对基础列表进行的变异操作不会报告给包装它的任何ObservableList的观察者。

这个Javadoc暗示了第二个问题。如果您在代码中执行的操作不同,则表示您将元素添加到ArrayList字段(名为allParts)。因此,ObservableList永远不会发现任何更改,因此没有更改事件被触发。更改事件的触发在ObservableList中编码。如果您希望收到更改通知,您只能通过包裹ObservableList的{​​{1}}访问列表

在这种情况下,您的代码仍然有用(当您致电ArrayList时),如果不是因为您还将tableView.refresh()包裹在ObservableList中。 FilteredList会创建支持FilteredList的“视图”。如果支持ObservableList从未触发任何更改,则ObservableList永远不会通知任何更改,这意味着它永远不会知道更新此“视图”。这意味着当您向FilteredList添加元素时,这些新元素位于“视图之外”(如果您要替换“视图内部”的元素,则更改将是可见的)

您可以使用以下代码查看此行为:

ArrayList

打印出来:

import java.util.*;
import javafx.collections.*;
import javafx.collections.transformation.*;

public class Main {

    public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("World");

        ObservableList<String> obsList = FXCollections.observableList(list);
        FilteredList<String> filteredList = new FilteredList<>(obsList);

        System.out.println("INITIAL STATE");
        System.out.printf("\tList: %s%n", list);
        System.out.printf("\tFilteredList: %s%n", filteredList);

        list.add("Goodbye");
        list.add("World");

        System.out.println("AFTER ADDING ELEMENTS");
        System.out.printf("\tList: %s%n", list);
        System.out.printf("\tFilteredList: %s%n", filteredList);

        list.set(0, "Foo");
        list.set(1, "Bar");

        System.out.println("AFTER REPLACING ELEMENTS");
        System.out.printf("\tList: %s%n", list);
        System.out.printf("\tFilteredList: %s%n", filteredList);
    }

}

考虑到所有这些因素,修复代码的最简单方法是将INITIAL STATE List: [Hello, World] FilteredList: [Hello, World] AFTER ADDING ELEMENTS List: [Hello, World, Goodbye, World] FilteredList: [Hello, World] AFTER REPLACING ELEMENTS List: [Foo, Bar, Goodbye, World] FilteredList: [Foo, Bar] 设为allParts。否则,您必须注意仅使用围绕ObservableList创建的ObservableList

修改

你还提到,“如果我将搜索文本字段留空并点击搜索按钮,则第4部分显示”。我想解决这个问题。以下是您的代码,其中说明了当您点击搜索时新ArrayList显示在Part中的原因:

TableView

因此,当您搜索时看到新的/* * I extracted the FXCollections.observableArrayList(Collection) out of the FilteredList * constructor to more easily see what is going on. */ /* * You create a **new** ObservableList using FXCollections.observableArrayList(Collection). This basically * creates a *snapshot* of the List returnd by getAllParts() as it currently is. At this point the 4th * Part is in that returned List. This means the newly created ObservableList will also contian the new * Part (since observableArrayList(Collection) copies the data). However, the *old* ObservableList that * was already set on the TableView *does not* contain that 4th Part. */ ObservableList<Part> parts = FXCollections.observableArrayList(inventory.getAllParts()); // You create a FilteredList that performs no filtering around "parts". Note // here that a Predicate that always returns true is equivalent to passing null // as the Predicate. FilteredList<Part> filteredData = new FilteredList<>(parts, p -> true); // Get the search criteria String newValue = txtPartSearch.getText(); filteredData.setPredicate(part -> { if (newValue == null || newValue.isEmpty()) { return true; // don't filter if there is no search criteria // since the newValue will be null or blank in this // case no Parts are filtered } // filter based on lower-case names and search critiera String lowerCaseFilter = newValue.toLowerCase(); if (part.getName().toLowerCase().contains(lowerCaseFilter)) { return true; } // else filter by ID return Integer.toString(part.getPartID()).equals(lowerCaseFilter); }); // Wrap the FilteredList in a SortedList and bind the comparatorProperty to // the comparatorProperty of the TableView (allows sorting by column). SortedList<Part> sortedData = new SortedList<>(filteredData); sortedData.comparatorProperty().bind(tblParts.comparatorProperty()); // Set the sortedData to the TableView tblParts.setItems(sortedData); 显示的根本原因是您每次搜索时都在创建 Part。这个新ObservableList的最新状态为ObservableList getAllParts()。另外,正如我在评论中已经提到的,您的编辑基本上与排序代码完全相同。既然你这样做:

List

在创建inventory.addPart(new Part(6,"Test",5.23,4,2,8)); tblParts.setItems(FXCollections.observableArrayList(inventory.getAllParts())); 之前添加Part 。同样,ObservableList获取FXCollections.observableArrayList(Collection)快照,当在此处调用该方法时,它包含新的Collection。如果您要将代码翻转为:

Part

然后tblParts.setItems(FXCollections.observableArrayList(inventory.getAllParts())); inventory.addPart(new Part(6, "Test", 5.23, 4, 2, 8)); 的{​​{1}}属性不会包含新的TableView。但是,items 中的Part allParts