繁体   English   中英

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

[英]JavaFX TableView rows not refreshing when list item added

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

当我添加零件时,它可以很好地添加到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()));

}

库存类具有静态的零件零件清单

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中调用方法来设置表,但是除非我调用search方法,否则表永远不会刷新。

编辑:

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

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

您的代码有几处错误。 最相关的是您使用FXCollections.observableArrayList(Collection) 此方法的Javadoc说明:

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

换句话说,它将Collection 复制到由ArrayList支持的新ObservableList中。 对原始Collection任何更新甚至都不会添加到ObservableList 您应该使用FXCollections.observableList(List) ,如果你想传递的List是后盾List

FXCollections.observableList(List)的Javadoc(重点是我的):

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

该Javadoc暗示了第二个问题。 除非您没有在代码中做不同的事情,否则您不会将其添加到ArrayList字段(名为allParts) 因此, ObservableList从不知道任何更改,因此不会触发任何更改事件。 更改事件的触发在ObservableList编码。 如果要通知您更改,则只能通过包装ArrayListObservableList访问列表。

在这种情况下,如果不是因为您也将ObservableList包裹在FilteredList ,则您的代码仍然可以工作(当您调用tableView.refresh() )。 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]

考虑到所有这些,修复代码的最简单方法就是将allPartsObservableList 否则,您必须注意仅使用在ArrayList周围创建的ObservableList


编辑

您还提到:“如果我将搜索文本字段保留为空白并单击搜索按钮,则会显示第4部分”。 我想解决这个问题。 这是您的代码,并提供了解释,当您单击搜索时,为什么新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()));

在创建ObservableList之前添加了Part 再次, FXCollections.observableArrayList(Collection)采取的快照 Collection ,当该方法被称为在这里,包含了新的Part 如果要将代码翻转至:

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

然后TableViewitems属性将不包含新的Part 但是, inventoryallParts ArrayList 将为

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM