[英]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
编码。 如果要通知您更改,则只能通过包装ArrayList
的ObservableList
访问列表。
在这种情况下,如果不是因为您也将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]
考虑到所有这些,修复代码的最简单方法就是将allParts
为ObservableList
。 否则,您必须注意仅使用在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));
然后TableView
的items
属性将不包含新的Part
。 但是, inventory
的allParts
ArrayList
将为 。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.