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