0

我有一个的SetProperty的SetProperty不会触发更改事件

private final SetProperty<String> origins = new SimpleSetProperty<>(FXCollections.observableSet(new TreeSet<>())); 

如果我修改此属性这一切的方式按预期工作(我的TableView被通知的变化)

this.origins.setValue(FXCollections.observableSet(new LinkedHashSet<>(origins))); 

如果我修改此属性这样,我的TableView不通知

this.origins.clear(); 
this.origins.addAll(origins); 

由于此属性的值是一个ob可预见的集合,我期望SetProperty被通知关于底层集合的变化,并且将这个变化事件传播给它的监听器(在这种情况下是TableView)。

的TableView中创建如下: FXML:

[..] 
<TableView fx:id="table" GridPane.columnIndex="0" 
GridPane.rowIndex="1" editable="true" 
GridPane.hgrow="always" GridPane.vgrow="always"> 
<columns> 
    <TableColumn text="Files"> 
     <cellValueFactory> 
      <PropertyValueFactory property="origins" /> 
     </cellValueFactory> 
    </TableColumn> 
</columns> 
</TableView> 
[..] 

的数据bean看起来是这样的:

public abstract class AbstractPeakBean extends VeryAbstractPeakBean implements PeakBean { 


private final SetProperty<String> origins = new SimpleSetProperty<>(FXCollections.observableSet(new TreeSet<>())); 


public void addAllOrigins(Collection<String> origins) { 

    this.origins.addAll(origins); 
} 

@Override 
public AbstractPeakBean addOrigin(String origin) { 

    this.origins.add(origin); 
    return this; 
} 

@Override 
public Collection<String> getOrigins() { 

    return Collections.unmodifiableCollection(origins); 
} 

@Override 
public SetProperty<String> originsProperty() { 

    return origins; 
} 

@Override 
public AbstractPeakBean setAllOrigins(Collection<String> origins) { 

    this.origins.clear(); 
    this.origins.addAll(origins); 
    this.origins.setValue(FXCollections.observableSet(new LinkedHashSet<>(origins))); 
    return this; 
} 

public void setOrigins(Collection<String> origins) { 

    this.origins.clear(); 
    this.origins.addAll(origins); 
} 
} 
+2

您没有提供允许我们重现该问题的代码。此外还不清楚,“TableView”如何与集合一起工作......另外,包含相同元素的集合可以很容易地与原始集合相同...... – fabian

回答

0

我做了一个小例子来测试你的问题,它按预期工作:

public static void main(String[] args) { 
    SetProperty<String> origins = new SimpleSetProperty<>(FXCollections.observableSet(new TreeSet<>())); 
    origins.addListener((obs, ov, nv) -> System.out.println("ChangeListener was called")); 
    origins.addListener((SetChangeListener.Change<?> change) -> System.out.println("SetListener was called")); 

    origins.set(FXCollections.observableSet(new TreeSet<>())); 
    origins.get().add("Test1"); 
    origins.get().add("Test1"); 
    origins.get().clear(); 
    origins.get().clear(); 
} 

正如你所看到的,有两种听众的标准ChangeListener和一个特殊的SetChangeListener,它可以在一个set属性上注册。 每当列表被修改(添加或移除动作)或由新的ObservableList替换时,标准ChangeListener触发。 SetChangeListener仅在列表被修改时触发。

其结果是:

ChangeListener was called 
ChangeListener was called 
SetListener was called 
ChangeListener was called 
SetListener was called 

从该示例中的第一动作是替换整个列表的。这会导致调用所有ChangeListeners,但不会调用SetChangeListeners。

第二个操作将“Test1”添加到集合,并且两个侦听器都被执行。

第三个操作不会修改集合,因为“Test1”已经是集合的成员,并且集合不包含重复项目。没有改变 - >没有改变事件。

第4个动作从集合中删除“Test1”,并且执行两个侦听器。

第五个动作确实(再次)不修改该集合,因为它已经是空的。


更新1

我做了一个小例子程序,我可以改变当前目录和交换表视图中的没有问题的整个列表:

import javafx.application.Application; 
import javafx.beans.property.ReadOnlyObjectWrapper; 
import javafx.collections.FXCollections; 
import javafx.collections.ObservableList; 
import javafx.scene.Scene; 
import javafx.scene.control.Button; 
import javafx.scene.control.TableColumn; 
import javafx.scene.control.TableView; 
import javafx.scene.layout.VBox; 
import javafx.stage.Stage; 

public class Main extends Application { 

    private VBox root; 
    private TableView<String> tableView; 
    private TableColumn<String, Character> lettersColumn; 
    private ObservableList<String> items; 
    private Scene scene; 
    private Button button1; 
    private Button button2; 

    @Override 
    public void start(Stage primaryStage) { 
     items = FXCollections.observableArrayList(); 
     items.addAll("Aa", "Bb", "Cc"); 
     tableView = new TableView<>(items); 
     lettersColumn = new TableColumn<>("FirstLetters"); 
     lettersColumn.setCellValueFactory(cellDataFeature -> 
      new ReadOnlyObjectWrapper<>(cellDataFeature.getValue().charAt(0)) 
     ); 

     tableView.getColumns().add(lettersColumn); 

     button1 = new Button("Add item"); 
     button1.setOnMouseClicked(event -> items.add("Dd")); 

     button2 = new Button("Swap whole list"); 
     button2.setOnMouseClicked(event -> { 
      items = FXCollections.observableArrayList(); 
      items.addAll("1a", "2b", "3c"); 
      tableView.setItems(items); 
     }); 

     root = new VBox(tableView, button1, button2); 
     scene = new Scene(root); 
     primaryStage.setScene(scene); 
     primaryStage.show(); 
    } 

    public static void main(String[] args) { 
     launch(args); 
    } 
} 

我希望这有助于找到你的问题。


更新2 我找到SimpleMapProperty和SimpleSetProperty的错误。如果您有多个ChangeListener,并且同时没有在您的属性上注册MapChangeListener/SetChangeListener,则不会执行Listener。这是MapExpressionHelper和SetExpressionHelper中的一个错误。

如果你想解决它只是注册一个空的Map/SetChangeListener,它会再次工作。例如:

setProperty.addListener((SetChangeListener.Change<?> change) -> {}); 
+0

非常感谢您的解释!您知道当通过FXML配置的TableView使用何种类型的侦听器时(如上所示),它们在SetProperty上注册了吗? – kerner1000

+0

嗯,我只在TabelView类中找到一个WeakListChangeListener。但是因为TableView允许交换整个List(setItems),所以它也必须注意这些事件。 – Zerlono

+0

你的问题解决了吗? – Zerlono