我有一个奇怪的问题,我确定这是我的代码问题,但无法找到。
问题: 我有一个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并做一些事情。
先谢谢。
答案 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
,然后您可以重复使用该列表也用于在循环中创建列。但我对你的设置知之甚少,不知道是否可能。