【问题标题】:JavaFX FilteredList, filtering based on property of items in the listJavaFX FilteredList,根据列表中项目的属性进行过滤
【发布时间】:2017-11-16 17:09:31
【问题描述】:

我有一种情况,我需要根据项目的某些属性(即条件是内部而不是外部)过滤 ObservableList<Item>。我发现 javafx 有FilteredList 所以我试了一下。我可以设置谓词和过滤工作,直到确定过滤的属性值发生变化。现在设置谓词如下:

list.setPredicate(t -> !t.filteredProperty().get())

由于谓词返回 boolean 而不是 BooleanProperty,因此对该属性的更改不会反映在列表中。

有什么简单的解决方法吗?我可以尝试做一些解决方法,例如创建一个单独的列表并同步它,或者每次在一个项目中的属性更改时重置谓词,希望重新触发过滤,但我首先想问一下,以防有人知道一个很好的解决方案,因为这些解决方法肯定不是。

【问题讨论】:

  • 更改房源时,再次拨打list.setPredicate(t -> !t.filteredProperty().get())即可。
  • 感谢您的想法。该列表是模型的一部分,只要有新数据就会更新。显示数据的视图不知道何时发生这种情况,但我想我可以以某种方式将其作为模型中的一个事件......我想没有实际的方法可以从列表中做这种事情,因为我需要添加例如列表中每个项目/属性的 InvalidationListener。
  • FXCollections 有一个工厂方法可以为你创建这样一个失效监听器。

标签: javafx javafx-8


【解决方案1】:

使用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();
        }
    }
}

【讨论】:

  • 哇,很好用 :),谢谢!需要注意的是,这似乎仅在实际列表未包含在 FXCollections.unmodifiableObservableList 中时才有效(我的模型在从数据更新的位置之外是只读的)。在使用实际列表而不是不可修改的包装之后,这可以工作。对我来说已经足够了:)
  • 这看起来像一个错误;但是请注意,FilteredList 无论如何都是不可修改的,因此如果您只能公开FilteredList,则可以实现相同程度的封装。
  • 是否可以在提取器中设置两个属性?我想创建两个FilteredList,但每个都应该过滤不同的属性。
  • 刚刚解决了:new javafx.beans.Observable[] {item.filteredProperty1(), item.filteredProperty2()});
【解决方案2】:

如果您正在使用数据库加载功能并且需要过滤多个字段,此解决方案将有所帮助。

 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
            });
    }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-10-18
    • 1970-01-01
    • 2022-01-08
    • 1970-01-01
    • 1970-01-01
    • 2015-02-26
    相关资源
    最近更新 更多