2015-07-28 2 views
2

Вот код:ChangeListener не давая правильное старое значение

package sample; 

import javafx.beans.InvalidationListener; 
import javafx.beans.Observable; 
import javafx.beans.property.ListProperty; 
import javafx.beans.property.SimpleListProperty; 
import javafx.beans.value.ChangeListener; 
import javafx.beans.value.ObservableValue; 
import javafx.collections.FXCollections; 
import javafx.collections.ListChangeListener; 
import javafx.collections.ObservableList; 

/** 
* Created by IDEA on 29/07/15. 
*/ 
public class ListPropertyTest { 
    public static void main(String[] args) { 
     ListProperty<String> lp = 
       new SimpleListProperty<>(FXCollections.observableArrayList()); 
     lp.addListener(new ListChangeListener<String>() { 
      @Override 
      public void onChanged(Change<? extends String> c) { 
       while(c.next()) { 
        String action = c.wasPermutated() ? "perm" 
          : c.wasUpdated() ? "upd" 
          : c.wasRemoved() ? "rem" 
          : c.wasAdded() ? "add" : ""; 
        System.out.println("Action: " + action); 
        System.out.println("Removed: " + c.getRemoved()); 
        System.out.println("Added: " + c.getAddedSubList()); 
       } 
      } 
     }); 

     lp.addListener(new ChangeListener<ObservableList<String>>() { 
      @Override 
      public void changed(ObservableValue<? extends ObservableList<String>> observable, ObservableList<String> oldValue, ObservableList<String> newValue) { 
       System.out.println("List changed."); 
       System.out.println("Old: " + oldValue); 
       System.out.println("New: " + newValue); 
      } 
     }); 

     lp.addListener(new InvalidationListener() { 
      @Override 
      public void invalidated(Observable observable) { 
       System.out.println("List invalid"); 
      } 
     }); 

     System.out.println("Add ========="); 
     lp.addAll("one", "two"); 
     System.out.println("Set ========="); 
     lp.set(FXCollections.observableArrayList("two", "three")); 
     System.out.println("Remove ============"); 
     lp.remove("two"); 
    } 
} 

Результат:

Add ========= 
List invalid 
List changed. 
Old: [one, two] 
New: [one, two] 
Action: add 
Removed: [] 
Added: [one, two] 
Set ========= 
List invalid 
List changed. 
Old: [one, two] 
New: [two, three] 
Action: rem 
Removed: [one, two] 
Added: [two, three] 
Remove ============ 
List invalid 
List changed. 
Old: [three] 
New: [three] 
Action: rem 
Removed: [two] 
Added: [] 

Как вы можете видеть, изменение слушателя вели себя корректно только в «Set» части.

+0

ну, пока вы не указываете, что вы ожидаете, вы попадаете в глубокую яму, см. Мой комментарий к @James_D [ответ] (http://stackoverflow.com/a/31689191/203657) ;-) – kleopatra

ответ

2

Большая часть этого предназначено для поведения.

A ListProperty является как ObjectProperty<ObservableList>, так и ObservableList.

Будучи ObjectProperty<ObservableList> означает, что обертывания (содержит ссылку на) в ObservableList и имеет setValue(ObservableList) (или set(...)) и getValue() методы. Как ObjectProperty<ObservableList>, вы можете зарегистрировать ChangeListener<ObservableList>. Если ссылка на обернутых список изменения, кто-то вызывающим setValue(ObservableList) метод ChangeListener<ObservableList>.changed(...) вызывается на любом зарегистрированном ChangeListener, передавая ссылку на ListProperty самой, старой ObservableList ссылки, и новый ObservableList ссылки. Вы видите это поведение, когда вы вызываете set в свой код.

Однако, когда вы просто меняете содержимое списка, обернутый список остается тем же физическим объектом. Таким образом, «старый список» и «новый список» - одно и то же. Это bug, что прослушиватель изменений вызывает в этом случае уведомления; хэт-кончик @kleopatra для указания этого.

ListProperty также реализует ObservableList, путем передачи методы ObservableList вызовов обернутого экземпляра списка. Это означает, что если содержимое списка изменится, все ListChangeListener s, зарегистрированные с ListProperty, получат уведомление. Опять же, обратите внимание в этом случае, что существует только один объект списка; это просто, что его содержимое было изменено.

Вы можете получить подробную информацию об изменениях, которые происходят на ObservableList через Change объект передается методу onChanged(...), как вы наблюдать в выходе из метода onChanged. Обратите внимание, что c.getFrom() и c.getTo() также предоставят вам индексы элементов, которые также изменились.

У меня почти нет необходимости в ListProperty: в подавляющем большинстве случаев использования мне достаточно просто использовать простой вариант ObservableList. Элементы управления, которые основаны на списках объектов (например,), используют их, поэтому вы можете позвонить setItems(...), чтобы передать в существующий ObservableList, если вам нужно, или (возможно, чаще всего) вы можете просто позвонить getItems(), чтобы получить текущий список и измените его. Как видно из приведенного выше объяснения, ListProperty позволяет этим элементам управления наблюдать любые изменения. Но вполне вероятно, что в вашем собственном коде вы будете использовать ObservableList прямо гораздо чаще, чем используете ListProperty.

+1

a) это [ошибка] (https://bugs.openjdk.java.net/browse/JDK-8095404), что ChangeListener запускает изменения в базовом списке b) используя ListProperty (vs.простой список ObservableProperty) является [единственным вариантом] (https://github.com/kleopatra/swingempire-fx/wiki/List-Valued-Property-Woes) (кроме регистрации InvalidationListener), чтобы надежно получать уведомление (это была основная причина для целого ряда ошибок вокруг selectModels, fi https://bugs.openjdk.java.net/browse/JDK-8089169) – kleopatra

+1

Для a), мне было интересно некоторое время после Я написал, почему уведомления были уволены. Я уточню ответ: спасибо за это. Для b), да, это правильно, но в клиентском коде редко бывает необходимо разрешить изменения личности в списке (по моему опыту, в любом случае: ymmv) –

+0

да, пробег для b), конечно, меняется - но разработчики должны знать о предостережении :-) – kleopatra

Смежные вопросы