SetProperty不会触发更改事件

时间:2016-10-28 10:12:09

标签: javafx observablecollection changelistener valuechangelistener

我有一个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);

由于此属性的值是一个可观察的集合,因此我希望SetProperty能够通知底层集合的更改,并将此更改事件传播给它的侦听器(在本例中为TableView)。 / p>

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);
}
}

1 个答案:

答案 0 :(得分: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。

第二个动作添加&#34; Test1&#34;到集合并且两个侦听器都被执行。

第三个动作不会修改集合,因为&#34; Test1&#34;已经是集合的成员,集合不包含重复项。没有变化 - &gt;没有改变事件。

第4个动作删除&#34; Test1&#34;从集合和两个侦听器都被执行。

第5个动作(再次)没有修改集合,因为它已经是空的。

更新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,则不会执行监听器。这是MapExpressionHelper和SetExpressionHelper中的一个错误。

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

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