2016-08-05 124 views
1

我有一个奇怪的问题,我敢肯定这是我的代码问题,但无法找到答案。javafx谓词只有第一次触发

问题: 我有一个javafx TableView,它由一个过滤器列表支持,并由一个可观察列表支持。我的要求是根据用户在文本字段中输入的输入过滤tableview数据。所以我在文本字段的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直接附加到文本字段的textProperty。

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

在此先感谢。

回答

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 s仅在价值实际更改时才会收到通知。

在您的例子(第二代码块),只要在搜索领域的变化的文字,你叫

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,然后您可以重新使用该列表来创建循环中的列。但是我对你的设置知之甚少,不知道这是否可行。

+0

太棒了,我没有申请n检查你的解决方案,但它是有道理的。让我尝试一下。也抱歉提出多个问题。我从现在开始纠正自己。 –

+0

嘿,我正在尝试你的解决方案。解决第一个问题。第二个问题。循环所有列,我无法说tradeProperties()。stream()。anyMatch(p - > p.contains(filterText)); p.contains不起作用。我无法调用p谓词上的contains方法。 –

+0

谢谢詹姆斯。有效。非常感谢。你不介意看看http://stackoverflow.com/questions/38801302/design-to-keep-helper-methods-in-javafx-project –