javafx谓词只是第一次触发

时间:2016-08-05 07:54:49

标签: java-8 javafx-8

我有一个奇怪的问题,我确定这是我的代码问题,但无法找到。

问题: 我有一个javafx TableView,它由一个过滤列表支持,后者又由一个可观察列表支持。我的要求是根据用户在文本字段中输入的输入过滤tableview数据。所以我在textfield的textProperty上附加了一个失效监听器,并且我正在尝试根据我的业务条件设置过滤列表的谓词。这就好了。

见下面的代码:

externalTradeTableViewDataFilterTextField.textProperty().addListener((obs) -> { 
//externalTradesFilteredList.setPredicate(somePredicate);
String filterText = externalTradeTableViewDataFilterTextField.getText().trim().toLowerCase();
externalTradesFilteredList.setPredicate((ExternalTrade anExternalTrade) -> {
if(filterText == null || filterText.isEmpty() || filterText.equals(""))
return true;
if(anExternalTrade.getOid().toString().contains(filterText))
return true;
else 
if(anExternalTrade.getExternalTradeSourceOid().getExternalTradeSrcName().toLowerCase().contains(filterText))
return true;
else 
if(anExternalTrade.getExternalTradeStatusOid().getExternalTradeStatusName().toLowerCase().contains(filterText))
return true;
else 
if(anExternalTrade.getExternalTradeStateOid().getExternalTradeStateName().toLowerCase().contains(filterText))
return true;
return false;
});
});

由于我达到了我的要求,我开始专注于代码重构。因此计划将谓词逻辑移动到单独的谓词并将谓词移动到另一个类,以便我可以重用它。 问题从这里开始。

见下面的代码:

externalTradeTableViewDataFilterTextField.textProperty().addListener((obs) -> { 
externalTradesFilteredList.setPredicate(somePredicate);
});

private Predicate<ExternalTrade> somePredicate = (ExternalTrade anExternalTrade) -> {
String filterText = externalTradeTableViewDataFilterTextField.getText().trim().toLowerCase();

if(filterText.isEmpty() || filterText == null || filterText.equals(""))
            return true;
if(anExternalTrade.getOid().toString().contains(filterText))
       return true;
else if(anExternalTrade.getExternalTradeSourceOid().getExternalTradeSrcName().toLowerCase().contains(filterText))
            return true;
else if(anExternalTrade.getExternalTradeStatusOid().getExternalTradeStatusName().toLowerCase().contains(filterText))
            return true;
        else if(anExternalTrade.getExternalTradeStateOid().getExternalTradeStateName().toLowerCase().contains(filterText))
            return true;
return false;
};

现在,每当我在文本字段中输入新文本时,我的侦听器就会被执行,但是谓词逻辑只是第一次被调用。从第二次起,谓词(somePredicate)没有被调用。

请帮帮我。还要建议我的代码是否良好或是否有更好的方法来实现这一目标,并获得更好的性能。 bcoz我确实看到一些代码使用绑定说过滤列表的filteredProperty直接附加到textfield的textProperty。

还有一件事。谓词内部的逻辑是检查文本是否包含在任何列中,如果返回行。我有20列。所以我需要为所有20列或任何其他方式编写if条件。 for for循环是唯一的方法吗?或者我可以使用.foreach并做一些事情。

先谢谢。

1 个答案:

答案 0 :(得分:1)

过滤器不会重新计算的原因是,从FilteredList的角度来看,它并没有改变。

在伪代码中,FilteredList可能看起来像这样:

public class FilteredList<T> {

    private ObjectProperty<Predicate<T>> predicate = new SimpleObjectProperty<>();

    private ObservableList<T> source ;

    public FilteredList<T>(ObservableList<T> source, Predicate<T> predicate) {

        this.source = source ;

        this.predicate.addListener((obs, oldPredicate, newPredicate) ->
            redoFilter());

        this.predicate.set(predicate);
    }

    // ...
}

即。它使用谓词属性注册ChangeListener,如果更改则重新计算过滤器。 (在现实生活中,它可能比这复杂得多,但那个想法会在那里。)

同时,SimpleObjectProperty执行以下操作(同样,这是伪代码):

public class SimpleObjectProperty<T> implements Property<T> {

    private T value ;

    public void set(T value) {
        if (! Objects.equals(this.value, value)) {
            T oldValue = this.value ;
            this.value = value ; 
            notifyChangeListeners(oldValue, this.value);
        }
    }
}

换句话说,ChangeListener仅在值实际更改时得到通知。

在您的示例(第二个代码块)中,只要搜索字段中的文本发生更改,您就会调用

externalTradesFilteredList.setPredicate(somePredicate);

每次使用完全相同的引用somePredicate。因此,当包含该谓词的筛选列表中的属性进行检查时,它看不到任何更改(完全相同的对象...),因此更改侦听器未被触发,因此筛选后的列表不知道它具有更新。

实际上,您没有更改谓词,只是更改了现有谓词的内部状态。

要修复,您可以执行以下操作:

private Predicate<ExternalTrade> createPredicate() {
    return (ExternalTrade anExternalTrade) -> {
        String filterText = externalTradeTableViewDataFilterTextField.getText().trim().toLowerCase();

        if(filterText.isEmpty() || filterText == null || filterText.equals(""))
                    return true;
        if(anExternalTrade.getOid().toString().contains(filterText))
               return true;
        else if(anExternalTrade.getExternalTradeSourceOid().getExternalTradeSrcName().toLowerCase().contains(filterText))
                    return true;
        else if(anExternalTrade.getExternalTradeStatusOid().getExternalTradeStatusName().toLowerCase().contains(filterText))
                    return true;
                else if(anExternalTrade.getExternalTradeStateOid().getExternalTradeStateName().toLowerCase().contains(filterText))
                    return true;
        return false;
    };

}

externalTradeTableViewDataFilterTextField.textProperty().addListener((obs) ->  
    externalTradesFilteredList.setPredicate(createPredicate()));

或等效地创建一个类:

private static class TradeTableFilter implements Predicate<ExternalTrade> {

    private final String filterText ;

    TradeTableFilter(String filterText) {
        this.filterText = filterText ;
    }

    @Override
    public boolean test(ExternalTrade anExternalTrade) {

        if(filterText.isEmpty() || filterText == null || filterText.equals(""))
                    return true;
        if(anExternalTrade.getOid().toString().contains(filterText))
               return true;
        else if(anExternalTrade.getExternalTradeSourceOid().getExternalTradeSrcName().toLowerCase().contains(filterText))
                    return true;
        else if(anExternalTrade.getExternalTradeStatusOid().getExternalTradeStatusName().toLowerCase().contains(filterText))
                    return true;
                else if(anExternalTrade.getExternalTradeStateOid().getExternalTradeStateName().toLowerCase().contains(filterText))
                    return true;
        return false;
    }

}

然后当然做

externalTradeTableViewDataFilterTextField.textProperty().addListener((obs) ->  
    externalTradesFilteredList.setPredicate(new TradeTableFilter(externalTradeTableViewDataFilterTextField.getText().trim().toLowerCase()));

对于你的其他问题(顺便说一句,你不应该在这个论坛上真正将多个问题合并为一个:这使得其他用户很难(或不可能)找到相同问题的现有答案),那里没有非常容易解决。

您可以在模型中创建Function<ExternalTrade, String>的属性列表:

private final List<Function<ExternalTrade, String>> tradeProperties = Arrays.asList(
    t -> t.getOid().toString(),
    t -> t.getExternalTradeSourceOid().getExternalTradeSrcName().toLowerCase(),
    t -> t.getExternalTradeStatusOid().getExternalTradeStatusName().toLowerCase(),
    t -> t.getExternalTradeStateOid().getExternalTradeStateName().toLowerCase()
);

然后

private Predicate<ExternalTrade> createPredicate() {
    return (ExternalTrade anExternalTrade) -> {
        String filterText = externalTradeTableViewDataFilterTextField.getText().trim().toLowerCase();
        return filterText == null ||
               filterText.isEmpty() ||
               tradeProperties().stream().anyMatch(p -> p.apply(anExternalTrade).contains(filterText));
    };
}

根据您的模型类以及如何设置表格,您可以在函数列表中映射到ObservableValue而不是String,然后您可以重复使用该列表也用于在循环中创建列。但我对你的设置知之甚少,不知道是否可能。