【问题标题】:JavaFX TableView filter using TextField使用 TextField 的 JavaFX TableView 过滤器
【发布时间】:2019-02-10 08:44:12
【问题描述】:

我想编写一个方法,我传递一个文本字段 (filterField)、来自 tableview (data) 和 tableview (table) 的数据,以便过滤所有表数据,检查每次在文本字段中释放一个键时,如果键入的字符串包含在表格的任何单元格中。

此方法是通用的,用于不同类型的具有不同列标题的表。

为此,我使用以下代码:

public void addTextFilter(ObservableList<List<Object>> allData, 
                           JFXTextField filterField, TableView<List<Object>> table) {

    FilteredList<List<Object>> filteredData  = new FilteredList<>(allData, p -> true);
    filterField.setOnKeyReleased(e -> 
    {
         filteredData.setPredicate(p  -> 
         {
             if (filterField.getText() == null || filterField.getText().isEmpty()){
                 return true;
             }else {
                 return p.contains(filterField.getText()); 
             }
         });


    });

    SortedList<List<Object>> sortedData = new SortedList<>(filteredData);
    sortedData.comparatorProperty().bind(table.comparatorProperty());
    table.setItems(sortedData);
}

此代码仅在文本字段中的字符串与表格中任何单元格的值完全匹配时返回值。

我需要它返回包含在键入时键入的字符串的表格的任何单元格,即使该单元格包含更多字符。类似的东西:JavaFX 8 TableView Sorting and Filtering

我还需要它返回不依赖于小写或大写值的值。

【问题讨论】:

    标签: javafx filter tableview


    【解决方案1】:

    您应该监听text 属性的变化,而不是使用KeyEvent

    TableColumns 可用于检索可在谓词中使用的单元格的值。

    public static <T> void addTextFilter(ObservableList<T> allData,
            JFXTextField filterField, TableView<T> table) {
    
        final List<TableColumn<T, ?>> columns = table.getColumns();
    
        FilteredList<T> filteredData = new FilteredList<>(allData);
        filteredData.predicateProperty().bind(Bindings.createObjectBinding(() -> {
            String text = filterField.getText();
    
            if (text == null || text.isEmpty()) {
                return null;
            }
            final String filterText = text.toLowerCase();
    
            return o -> {
                for (TableColumn<T, ?> col : columns) {
                    ObservableValue<?> observable = col.getCellObservableValue(o);
                    if (observable != null) {
                        Object value = observable.getValue();
                        if (value != null && value.toString().toLowerCase().equals(filterText)) {
                            return true;
                        }
                    }
                }
                return false;
            };
        }, filterField.textProperty()));
    
        SortedList<T> sortedData = new SortedList<>(filteredData);
        sortedData.comparatorProperty().bind(table.comparatorProperty());
        table.setItems(sortedData);
    }
    

    如果您的列包含列表的值,您可以稍微简化代码:

    public static void addTextFilter(ObservableList<List<Object>> allData,
            JFXTextField filterField, TableView<List<Object>> table) {
    
        final List<TableColumn<List<Object>, ?>> columns = table.getColumns();
    
        FilteredList<List<Object>> filteredData = new FilteredList<>(allData);
        filteredData.predicateProperty().bind(Bindings.createObjectBinding(() -> {
            String text = filterField.getText();
    
            if (text == null || text.isEmpty()) {
                return null;
            }
            final String filterText = text.toLowerCase();
    
            return o -> {
                for (Object value : columns) {
                    if (value != null && value.toString().toLowerCase().equals(filterText)) {
                        return true;
                    }
                }
                return false;
            };
        }, filterField.textProperty()));
    
        SortedList<List<Object>> sortedData = new SortedList<>(filteredData);
        sortedData.comparatorProperty().bind(table.comparatorProperty());
        table.setItems(sortedData);
    }
    

    请注意,上述代码 sn-ps 都不会收到对项目所做更改的通知。

    【讨论】:

    • 感谢 Fabian!它就像一个魅力。为了获取包含文本字段文本的所有单元格,我将“value!= null && value.toString().toLowerCase().equals(filterText)”更改为“value!= null && value.toString().toLowerCase ().contains(filterText)"
    【解决方案2】:

    本教程很好,使用 TextField 的可观察值:textProperty 而不是 Texfield 本身。只需根据您的情况进行调整即可。

    它会给你更少的耦合,并且正确使用 observable,你不会依赖图形事件(如果用户从鼠标而不是键盘点击和粘贴呢?)-> 更健壮

    public void addTextFilter(ObservableList<List<Object>> allData, 
                           StringProperty textProperty, TableView<List<Object>> table)     {
    
    
      FilteredList<List<Object>> filteredData  = new FilteredList<>(allData, p -> true);
      // Use textProperty
      textProperty.addListener((observable, oldValue, newValue) -> {
            filteredData.setPredicate(person -> {
                // If filter text is empty, display all persons.
                if (newValue == null || newValue.isEmpty()) {
                    return true;
                }
    
                // Compare first name and last name of every person with filter text.
                String lowerCaseFilter = newValue.toLowerCase();
    
                if (person.getFirstName().toLowerCase().contains(lowerCaseFilter)) {
                    return true; // Filter matches first name.
                } else if     (person.getLastName().toLowerCase().contains(lowerCaseFilter)) {
                    return true; // Filter matches last name.
                }
                return false; // Does not match.
            });
        });
    
      SortedList<List<Object>> sortedData = new SortedList<>(filteredData);
      sortedData.comparatorProperty().bind(table.comparatorProperty());
      table.setItems(sortedData);
    }
    

    【讨论】:

    • 感谢您的回答,但您只是将解决方案复制到我添加的链接中,无论如何这都行不通。请记住,链接解决方案是具有固定列和标题的固定表,因此可以这样编写代码,而不是在我公开的情况下。
    • 那么你为什么要更改教程的代码,因为它已经工作了!
    • 您的示例和教程有什么不同?为什么不直接使用 textProperty?
    • 如果您没有像示例中那样定义名为 person 的类,您将无法对 FirstName 和 LastName 使用预定义的 getter 方法,如图所示。这只是一个例子,不适用于我的情况。我的表将被动态创建,我不需要为每个表创建一个类,这太荒谬了!我需要动态评估存储在 FilteredList 中的数据,并将其与文本字段中输入的值进行比较。正如您可能理解的那样,问题在于存储在 FilteredList 中的数据(列/行)并不总是相同
    • 问题不在于使用 textProperty,而是如何在不知道表格的列的情况下过滤表格。
    【解决方案3】:

    对此我感到非常自豪,我认为这是我第一次在 StackOverflow 上回答问题。

    此代码将允许您搜索部分匹配项。因此,如果您输入字母 d,它将消除任何没有字母 d 的行。

    此代码还允许您在表格的每一行中搜索多个单词(以空格分隔)。因此,如果单词“dog”和“woof”都在行中,那么您将输入:

    狗汪汪

    它只会显示同时包含这两个单词的行。您可以使用任意数量的搜索词。

    public static void addTextFilterB(ObservableList<List<Object>> allData,
                                      TextField filterField, TableView<List<Object>> table) {
    
        FilteredList<List<Object>> filteredData  = new FilteredList<>(allData, p -> true);
        filterField.setOnKeyReleased(e ->
        {
            filteredData.setPredicate(p  ->
            {
                if (filterField.getText() == null || filterField.getText().isEmpty()){
                    return true;
                }else {
                    String pToString = p.toString().toLowerCase().replace(", "," ");
                    String textIwantB = filterField.getText();
                    String[] parts = textIwantB.toLowerCase().split(" ");
    
                    if(p.contains(textIwantB)){
                        System.out.println("p.: " + p);
    
                    }
    
                    int counter = 0;
                    for (int i = 0; i < parts.length; i ++) {
                        if (parts[i] != null)
                            if(!(pToString.contains(parts[i]))){
                                System.out.println("this one is eliminated: " + pToString);
                                return false;
                            }
                            counter++;
                    }
    
                    System.out.println("counter: " + counter);
    
    
    
    
                    return pToString.contains(parts[0]);
                }
            });
    
    
        });
    
        SortedList<List<Object>> sortedData = new SortedList<>(filteredData);
        sortedData.comparatorProperty().bind(table.comparatorProperty());
        table.setItems(sortedData);
    }
    

    【讨论】:

      猜你喜欢
      • 2017-08-13
      • 2018-07-18
      • 1970-01-01
      • 2012-11-08
      • 2017-09-26
      • 2018-09-28
      • 1970-01-01
      • 2018-08-06
      • 1970-01-01
      相关资源
      最近更新 更多