JavaFX FilteredList,基于列表中 属性 项的过滤
JavaFX FilteredList, filtering based on property of items in the list
我有一个情况需要根据项目的某些属性(即条件是内部的而不是外部的)过滤 ObservableList<Item>
。我发现 javafx 有 FilteredList
所以我试了一下。我可以设置谓词和过滤工作,直到确定过滤更改的 属性 值。设置谓词现在完成如下:
list.setPredicate(t -> !t.filteredProperty().get())
由于谓词 returns 是布尔值而不是 BooleanProperty,因此对 属性 的更改不会反映在列表中。
有什么简单的解决办法吗?我可以尝试做一些变通办法,例如创建一个单独的列表并同步它,或者每次 属性 更改一个项目时都希望重新触发过滤时重置谓词,但我首先想问一下是否有人知道一个很好的解决方案,因为这些解决方法肯定不是。
使用 extractor 创建基础列表。这将使基础列表能够在任何元素的 filteredProperty()
更改时触发更新事件。 FilteredList
将观察这些事件并相应更新:
ObservableList<Item> baseList = FXCollections.observableArrayList(item ->
new Observable[] {item.filteredProperty()});
FilteredList<Item> list = new FilteredList<>(baseList, t -> ! t.filteredProperty().get());
快速演示:
import java.util.stream.IntStream;
import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
public class DynamicFilteredListTest {
public static void main(String[] args) {
ObservableList<Item> baseList = FXCollections.observableArrayList(item ->
new Observable[] {item.filteredProperty()});
FilteredList<Item> list = new FilteredList<>(baseList, t -> ! t.isFiltered());
list.addListener((Change<? extends Item> c) -> {
while (c.next()) {
if (c.wasAdded()) {
System.out.println(c.getAddedSubList()+ " added to filtered list");
}
if (c.wasRemoved()) {
System.out.println(c.getRemoved()+ " removed from filtered list");
}
}
});
System.out.println("Adding ten items to base list:\n");
IntStream.rangeClosed(1, 10).mapToObj(i -> new Item("Item "+i)).forEach(baseList::add);
System.out.println("\nFiltered list now:\n"+list);
System.out.println("\nSetting filtered flag for alternate items in base list:\n");
IntStream.range(0, 5).map(i -> 2*i).mapToObj(baseList::get).forEach(i -> i.setFiltered(true));
System.out.println("\nFiltered list now:\n"+list);
}
public static class Item {
private final StringProperty name = new SimpleStringProperty() ;
private final BooleanProperty filtered = new SimpleBooleanProperty() ;
public Item(String name) {
setName(name);
}
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 BooleanProperty filteredProperty() {
return this.filtered;
}
public final boolean isFiltered() {
return this.filteredProperty().get();
}
public final void setFiltered(final boolean filtered) {
this.filteredProperty().set(filtered);
}
@Override
public String toString() {
return getName();
}
}
}
如果您正在使用数据库加载功能并且需要过滤多个字段,此解决方案将有所帮助。
ObservableList<PurchaseOrder> poData = FXCollections.observableArrayList();
FilteredList<PurchaseOrder> filteredData;
private void load() {
PurchaseOrder po = new PurchaseOrder();
try {
poData = po.loadTable("purchase_orders", beanFields); // Database loading data
} catch (SQLException ex) {
Logger.getLogger(PurchaseOrdersController.class.getName()).log(Level.SEVERE, null, ex);
}
filteredData = new FilteredList<>(poData, t -> true); //Encapsulate data with filter
poTable.setItems(filteredData); //Load filtered data into table
//Set event trigger for all filter textboxes
txtFilter.textProperty().addListener(obs->{
filter(filteredData);
});
txtFilter2.textProperty().addListener(obs->{
filter(filteredData);
});
}
private void filter(FilteredList<PurchaseOrder> filteredData)
{
filteredData.setPredicate(PurchaseOrder -> {
// If all filters are empty then display all Purchase Orders
if ((txtFilter.getText() == null || txtFilter.getText().isEmpty())
&& (txtFilter2.getText() == null || txtFilter2.getText().isEmpty())) {
return true;
}
// Convert filters to lower case
String lowerCaseFilter = txtFilter.getText().toLowerCase();
String lowerCaseFilter2 = txtFilter2.getText().toLowerCase();
//If fails any given criteria, fail completely
if(txtFilter.getText().length()>0)
if (PurchaseOrder.get("vendor_name").toLowerCase().contains(lowerCaseFilter) == false)
return false;
if(txtFilter2.getText().length()>0)
if (PurchaseOrder.get("PONumber").toLowerCase().contains(lowerCaseFilter2) == false)
return false;
return true; // Matches given criteria
});
}
我有一个情况需要根据项目的某些属性(即条件是内部的而不是外部的)过滤 ObservableList<Item>
。我发现 javafx 有 FilteredList
所以我试了一下。我可以设置谓词和过滤工作,直到确定过滤更改的 属性 值。设置谓词现在完成如下:
list.setPredicate(t -> !t.filteredProperty().get())
由于谓词 returns 是布尔值而不是 BooleanProperty,因此对 属性 的更改不会反映在列表中。
有什么简单的解决办法吗?我可以尝试做一些变通办法,例如创建一个单独的列表并同步它,或者每次 属性 更改一个项目时都希望重新触发过滤时重置谓词,但我首先想问一下是否有人知道一个很好的解决方案,因为这些解决方法肯定不是。
使用 extractor 创建基础列表。这将使基础列表能够在任何元素的 filteredProperty()
更改时触发更新事件。 FilteredList
将观察这些事件并相应更新:
ObservableList<Item> baseList = FXCollections.observableArrayList(item ->
new Observable[] {item.filteredProperty()});
FilteredList<Item> list = new FilteredList<>(baseList, t -> ! t.filteredProperty().get());
快速演示:
import java.util.stream.IntStream;
import javafx.beans.Observable;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
public class DynamicFilteredListTest {
public static void main(String[] args) {
ObservableList<Item> baseList = FXCollections.observableArrayList(item ->
new Observable[] {item.filteredProperty()});
FilteredList<Item> list = new FilteredList<>(baseList, t -> ! t.isFiltered());
list.addListener((Change<? extends Item> c) -> {
while (c.next()) {
if (c.wasAdded()) {
System.out.println(c.getAddedSubList()+ " added to filtered list");
}
if (c.wasRemoved()) {
System.out.println(c.getRemoved()+ " removed from filtered list");
}
}
});
System.out.println("Adding ten items to base list:\n");
IntStream.rangeClosed(1, 10).mapToObj(i -> new Item("Item "+i)).forEach(baseList::add);
System.out.println("\nFiltered list now:\n"+list);
System.out.println("\nSetting filtered flag for alternate items in base list:\n");
IntStream.range(0, 5).map(i -> 2*i).mapToObj(baseList::get).forEach(i -> i.setFiltered(true));
System.out.println("\nFiltered list now:\n"+list);
}
public static class Item {
private final StringProperty name = new SimpleStringProperty() ;
private final BooleanProperty filtered = new SimpleBooleanProperty() ;
public Item(String name) {
setName(name);
}
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 BooleanProperty filteredProperty() {
return this.filtered;
}
public final boolean isFiltered() {
return this.filteredProperty().get();
}
public final void setFiltered(final boolean filtered) {
this.filteredProperty().set(filtered);
}
@Override
public String toString() {
return getName();
}
}
}
如果您正在使用数据库加载功能并且需要过滤多个字段,此解决方案将有所帮助。
ObservableList<PurchaseOrder> poData = FXCollections.observableArrayList();
FilteredList<PurchaseOrder> filteredData;
private void load() {
PurchaseOrder po = new PurchaseOrder();
try {
poData = po.loadTable("purchase_orders", beanFields); // Database loading data
} catch (SQLException ex) {
Logger.getLogger(PurchaseOrdersController.class.getName()).log(Level.SEVERE, null, ex);
}
filteredData = new FilteredList<>(poData, t -> true); //Encapsulate data with filter
poTable.setItems(filteredData); //Load filtered data into table
//Set event trigger for all filter textboxes
txtFilter.textProperty().addListener(obs->{
filter(filteredData);
});
txtFilter2.textProperty().addListener(obs->{
filter(filteredData);
});
}
private void filter(FilteredList<PurchaseOrder> filteredData)
{
filteredData.setPredicate(PurchaseOrder -> {
// If all filters are empty then display all Purchase Orders
if ((txtFilter.getText() == null || txtFilter.getText().isEmpty())
&& (txtFilter2.getText() == null || txtFilter2.getText().isEmpty())) {
return true;
}
// Convert filters to lower case
String lowerCaseFilter = txtFilter.getText().toLowerCase();
String lowerCaseFilter2 = txtFilter2.getText().toLowerCase();
//If fails any given criteria, fail completely
if(txtFilter.getText().length()>0)
if (PurchaseOrder.get("vendor_name").toLowerCase().contains(lowerCaseFilter) == false)
return false;
if(txtFilter2.getText().length()>0)
if (PurchaseOrder.get("PONumber").toLowerCase().contains(lowerCaseFilter2) == false)
return false;
return true; // Matches given criteria
});
}