2

TableView の itemsProperty に ChangeListener を追加するとします。ChangeListener の changed メソッドはいつ呼び出されますか?

TableView の項目が指す空のリストに追加してみました。結果 - ChangeListener の changed メソッドは呼び出されませんでした。

tableView.itemsProperty().addListener(new ChangeListener() {
    @Override
    public void changed(ObservableValue ov, Object t, Object t1) {
        System.out.println("Changed!");
    }
});

final ObservableList data = FXCollections.observableArrayList(new ArrayList());
data.clear();
//data.add(new Object()); don't call this yet
tableView.setItems(data);
data.clear();
data.add(new Object());
tableView.setItems(data);

ただし、空のリストに追加してから、TableViewのアイテムがそれを指すようにしました。結果 - ChangeListener の changed メソッドが呼び出されました。

tableView.itemsProperty().addListener(new ChangeListener() {
    @Override
    public void changed(ObservableValue ov, Object t, Object t1) {
        System.out.println("Changed!");
    }
});

final ObservableList data = FXCollections.observableArrayList(new ArrayList());
data.clear();
data.add(new Object()); 
tableView.setItems(data);
data.clear();
data.add(new Object());
tableView.setItems(data);

http://docs.oracle.com/javafx/2/api/javafx/scene/control/TableView.html#itemsProperty()で調べましたが、「TableView の基礎となるデータ モデル。 TableView 自体の型と一致する必要があるジェネリック型があります。」

他の重要な状況を見逃す可能性があるため、これを求めています。

4

2 に答える 2

3

完全に文書化されていない事実 (別名: 実装の詳細) は、ObjectProperty のみが起動することです。

!oldValue.equals(newValue); // modulo null checking 

すべての要素が等しい場合、リストは等しいと指定されるため、これはリスト値オブジェクト プロパティにとって重要です。特に、すべての空のリストは互いに等しいため、最初のスニペットのように空のリストを別の空のリストに置き換えても、プロパティは発火しません。

 // items empty initially
 TableView table = new TableView()
 table.itemsProperty().addListener(....)
 ObservableList empty = FXCollections.observableArrayList();
 // replace initial empty list by new empty list
 table.setItems(empty);
 // no change event was fired!

コードがコンテンツの変更をリッスンしたい場合、これは厄介です。項目値の ID が変更されるたびに ListChangeListeners を再配線する必要がありますが、同等性に基づいて起動するため、changeListener を使用することはできません。ところで、fx-internal コードでさえ間違っていて、汚いハックによって修正されました

そして、適切な解決策はありません。最適ではないオプションがいくつかあります

  • changeListener の代わりに InvalidationListener を使用する
  • ListProperty をリスト値のオブジェクト プロパティに (一方向に!) バインドし、後者をリッスンします。
  • 上記を組み合わせたアダプターを使用して、少なくとも邪魔にならないようにする

私が使用するコードスニペット:

public static <T> ListProperty<T> listProperty(final Property<ObservableList<T>> property) {
    Objects.requireNonNull(property, "property must not be null");
    ListProperty<T> adapter = new ListPropertyBase<T>() {
        // PENDING JW: need weakListener?
        private InvalidationListener hack15793;
        {
            Bindings.bindBidirectional(this, property);
            hack15793 = o -> {
                ObservableList<T> newItems =property.getValue();
                ObservableList<T> oldItems = get();
                // force rewiring to new list if equals
                boolean changedEquals = (newItems != null) && (oldItems != null) 
                        && newItems.equals(oldItems);
                if (changedEquals) {
                    set(newItems);
                }
            };
            property.addListener(hack15793);
        }

        @Override
        public Object getBean() {
            return null; // virtual property, no bean
        }

        @Override
        public String getName() {
            return property.getName();
        }

        @Override
        protected void finalize() throws Throwable {
            try {
                Bindings.unbindBidirectional(property, this);
                property.removeListener(hack15793);
            } finally {
                super.finalize();
            }
        }

    };
    return adapter;
}
于 2014-10-18T08:50:06.883 に答える