2014-11-04 5 views
3

사용자 정의 속성을 작성하고이를 관찰 가능 목록에 추가합니다. 그러나 속성 내용이 변경되면 리스너가 호출되지 않습니다. 다음 코드 조각은 '건물'을 보여줍니다Java 8 : Observable List - 속성 변경의 경우 무효화 리스너 또는 변경 리스너가 호출됩니다.

public static final class TestObject { 
    private final ReadOnlyStringWrapper title = new ReadOnlyStringWrapper(); 
    private final BooleanProperty selected = new SimpleBooleanProperty(false); 

    public TestObject(String title) { 
     this.title.set(title); 
    } 

    public String getTitle() { 
     return title.get(); 
    } 

    public ReadOnlyStringProperty titleProperty() { 
     return title.getReadOnlyProperty(); 
    } 

    public boolean getSelected() { 
     return selected.get(); 
    } 

    public BooleanProperty selectedProperty() { 
     return selected; 
    } 

    public void setSelected(boolean selected) { 
     this.selected.set(selected); 
    } 

    @Override 
    public int hashCode() { 
     return Objects.hash(title.get()); 
    } 

    @Override 
    public boolean equals(Object obj) { 
     if (this == obj) { 
      return true; 
     } 
     if (obj == null || getClass() != obj.getClass()) { 
      return false; 
     } 
     final TestObject other = (TestObject) obj; 
     return Objects.equals(this.title.get(), other.title.get()); 
    } 

    @Override 
    public String toString() { 
     return "TestObject{" + 
       "title=" + title.get() + 
       ", selected=" + selected.get() + 
       '}'; 
    } 
} 

이 이름과 선택처럼 내 내부 속성 값을 내 POJO 클래스입니다.

public static final class TestProperty extends SimpleObjectProperty<TestObject> { 
    public TestProperty(String name) { 
     super(new TestObject(name)); 
     init(); 
    } 

    public TestProperty(TestObject testObject) { 
     super(testObject); 
     init(); 
    } 

    public String getTitle() { 
     return getValue().getTitle(); 
    } 

    public void setSelected(boolean selected) { 
     getValue().setSelected(selected); 
    } 

    public boolean getSelected() { 
     return getValue().getSelected(); 
    } 

    public BooleanProperty selectedProperty() { 
     return getValue().selectedProperty(); 
    } 

    public ReadOnlyStringProperty titleProperty() { 
     return getValue().titleProperty(); 
    } 

    @Override 
    public void set(TestObject testObject) { 
     super.set(testObject); 
     init(); 
    } 

    @Override 
    public void setValue(TestObject testObject) { 
     super.setValue(testObject); 
     init(); 
    } 

    private void init() { 
     if (get() == null) 
      return; 

     get().titleProperty().addListener((v, o, n) -> fireValueChangedEvent()); 
     get().selectedProperty().addListener((v, o, n) -> { 
      fireValueChangedEvent(); 
     }); 
    } 
} 

이것은 POJO를 기반으로하는 사용자 정의 속성입니다. 모든 속성을 변경하면 내 사용자 정의 속성에 대한 변경 이벤트가 발생합니다.

@Test 
public void testSimple() { 
    final AtomicInteger counter = new AtomicInteger(0); 
    final TestProperty testProperty = new TestProperty("Test"); 
    testProperty.addListener(observable -> { 
     System.out.println("New state: " + testProperty.get().toString()); 
     counter.incrementAndGet(); 
    }); 

    testProperty.setSelected(true); 
    testProperty.setSelected(false); 

    Assert.assertEquals(2, counter.intValue()); 
} 

이 테스트에서 속성 변경 이벤트가 제대로 작동하는지 확인할 수 있습니다.

@Test 
public void testList() { 
    final AtomicInteger counter = new AtomicInteger(0); 
    final ObservableList<TestProperty> observableList = new ObservableListWrapper<>(new ArrayList<>()); 
    observableList.add(new TestProperty("Test 1")); 
    observableList.add(new TestProperty("Test 2")); 
    observableList.add(new TestProperty("Test 3")); 

    observableList.addListener(new ListChangeListener<TestProperty>() { 
     @Override 
     public void onChanged(Change<? extends TestProperty> change) { 
      System.out.println("**************"); 
     } 
    }); 
    observableList.addListener((Observable observable) -> { 
     System.out.println("New state: " + ((TestProperty) observable).get().toString()); 
     counter.incrementAndGet(); 
    }); 

    observableList.get(1).setSelected(true); 
    observableList.get(2).setSelected(true); 
    observableList.get(1).setSelected(false); 
    observableList.get(2).setSelected(false); 

    Assert.assertEquals(4, counter.intValue()); 
} 

는하지만이 코드에서 당신은 속성 값이 목록에 변경된 경우 관찰 목록은 무효화 리스너도 변경 리스너를 호출되지 것을 알 수있다.

무엇이 잘못 되었나요?

감사합니다.

+0

(물론 질문과 밀접하게 관련되어 있음) 'ObservableListWrapper'는 공개 API의 일부가 아니며 실제로 사용되어서는 안됩니다. JavaFX의 향후 릴리스에는 이것이있을 것이라는 보장은 없습니다. 'ObservableList' (및 다른 관찰 가능한 콜렉션)를 생성하려면 ['FXCollections'] (http://docs.oracle.com/javase/8/javafx/api/javafx/collections/FXCollections.html)의 팩토리 메소드를 사용하십시오. . –

답변

15

목록의 요소 속성이 변경된 경우 "목록 업데이트 됨"알림을 보낼 관찰 가능 목록을 만들려면 extractor으로 목록을 만들어야합니다. extractor은 목록의 각 요소를 Observable의 배열로 매핑하는 Callback입니다. Observable이 변경되면 목록에 등록 된 InvalidationListenerListChangeListener이 통지됩니다.

제목을 변경할 수 있다면 그래서 testList() 방법, 당신은

final ObservableList<TestProperty> observableList = FXCollections.observableList(
    new ArrayList<>(), 
    (TestProperty tp) -> new Observable[]{tp.selectedProperty()}); 

을 수행 할 수 있습니다, 당신은 또한이 일어 났을 때 알림을 수신 할 수있는 목록을 원하는, 당신은 너무 할 수있는 :

final ObservableList<TestProperty> observableList = FXCollections.observableList(
    new ArrayList<>(), 
    (TestProperty tp) -> new Observable[]{tp.selectedProperty(), tp.titleProperty()}); 

추출기가 본질적으로 함수 인 Callback이기 때문에 구현이 임의로 복잡 할 수 있습니다 (다른 속성 값을 기준으로 한 속성을 조건부로 관찰 등).

+0

Person 클래스에 대해 ObservableList의 변경 내용을 수신하는 예제를 만들 수 있습니까?이 예에서는 90 %의 사례에 대한 간단한 예제를 제공하지 않습니다. –

+0

유망 해 보입니다. 다음과 같은 경우 명확한 설명을하십시오. http://stackoverflow.com/q/43745418/546476 –

+0

배열 목록 대신 연결된 목록에서 작동하지 않는 이유가 있습니까? – Ruben9922

0

ObservableList는 목록에 포함 된 속성이 수정 될 때마다 수신기에 알리지 않고 목록이 통지 될 때이를 알립니다.

이것은 당신이 테스트 수정할 때 볼 수 있습니다 :

@Test 
public void testList() { 
    final AtomicInteger counter = new AtomicInteger(0); 
    final ObservableList<TestProperty> observableList = new ObservableListWrapper<>(new ArrayList<>()); 

    observableList.addListener(new ListChangeListener<TestProperty>() { 
     @Override 
     public void onChanged(Change<? extends TestProperty> change) { 
      System.out.println("**************"); 
      counter.incrementAndGet(); 
     } 
    }); 

    observableList.add(new TestProperty("Test 1")); 
    observableList.add(new TestProperty("Test 2")); 
    observableList.add(new TestProperty("Test 3")); 

    observableList.get(1).setSelected(true); 
    observableList.get(2).setSelected(true); 
    observableList.get(1).setSelected(false); 
    observableList.get(2).setSelected(false); 

    Assert.assertEquals(3, counter.intValue()); 
} 

편집 : 방공호가 원하는대로 ObservableValue 변경 리스너의 자동 등록/해제를 제공하는 예 ObserverListener의 장식을 추가했습니다.

public class ObservableValueListWrapper<E extends ObservableValue<E>> extends ObservableListWrapper<E> { 
public ObservableValueListWrapper(List<E> list) { 
    super(list, o -> new Observable[] {o});}} 

또는 당신이 POJO로 목록을 작성해야합니다 :

final ObservableList<MyPOJO> list = new ObservableListWrapper<>(new ArrayList(), o -> new Observable[] { new MyPOJOProperty(o) }); 

또는 당신이 그것을 사용

/** 
* Decorates an {@link ObservableList} and auto-registers the provided 
* listener to all new observers, and auto-unregisters listeners when the 
* item is removed from the list. 
* 
* @param <T> 
*/ 
public class ObservableValueList<T extends ObservableValue> implements ObservableList<T> { 

    private final ObservableList<T> list; 
    private final ChangeListener<T> valueListener; 

    public ObservableValueList(ObservableList<T> list, ChangeListener<T> valueListener) { 
     this.list = list; 
     //list to existing contents of list 
     this.list.stream().forEach((item) -> item.addListener(valueListener)); 

     //register listener which will add/remove listner on change to list 
     this.list.addListener((Change<? extends T> change) -> { 
      change.getAddedSubList().stream().forEach(
        (item) -> item.addListener(valueListener)); 

      change.getRemoved().stream().forEach(
        (item) -> item.removeListener(valueListener)); 
     }); 
     this.valueListener = valueListener; 
    } 

    /* What follows is all the required delegate methods */ 

    @Override 
    public int size() { 
     return list.size(); 
    } 

    @Override 
    public boolean isEmpty() { 
     return list.isEmpty(); 
    } 

    @Override 
    public boolean contains(Object o) { 
     return list.contains(o); 
    } 

    @Override 
    public Iterator<T> iterator() { 
     return list.iterator(); 
    } 

    @Override 
    public Object[] toArray() { 
     return list.toArray(); 
    } 

    @Override 
    public <T> T[] toArray(T[] ts) { 
     return list.toArray(ts); 
    } 

    @Override 
    public boolean add(T e) { 
     return list.add(e); 
    } 

    @Override 
    public boolean remove(Object o) { 
     return list.remove(o); 
    } 

    @Override 
    public boolean containsAll(Collection<?> clctn) { 
     return list.containsAll(clctn); 
    } 

    @Override 
    public boolean addAll(Collection<? extends T> clctn) { 
     return list.addAll(clctn); 
    } 

    @Override 
    public boolean addAll(int i, Collection<? extends T> clctn) { 
     return list.addAll(i, clctn); 
    } 

    @Override 
    public boolean removeAll(Collection<?> clctn) { 
     return list.removeAll(clctn); 
    } 

    @Override 
    public boolean retainAll(Collection<?> clctn) { 
     return list.retainAll(clctn); 
    } 

    @Override 
    public void replaceAll(UnaryOperator<T> uo) { 
     list.replaceAll(uo); 
    } 

    @Override 
    public void sort(Comparator<? super T> cmprtr) { 
     list.sort(cmprtr); 
    } 

    @Override 
    public void clear() { 
     list.clear(); 
    } 

    @Override 
    public T get(int i) { 
     return list.get(i); 
    } 

    @Override 
    public T set(int i, T e) { 
     return list.set(i, e); 
    } 

    @Override 
    public void add(int i, T e) { 
     list.add(i, e); 
    } 

    @Override 
    public T remove(int i) { 
     return list.remove(i); 
    } 

    @Override 
    public int indexOf(Object o) { 
     return list.indexOf(o); 
    } 

    @Override 
    public int lastIndexOf(Object o) { 
     return list.lastIndexOf(o); 
    } 

    @Override 
    public ListIterator<T> listIterator() { 
     return list.listIterator(); 
    } 

    @Override 
    public ListIterator<T> listIterator(int i) { 
     return list.listIterator(i); 
    } 

    @Override 
    public List<T> subList(int i, int i1) { 
     return list.subList(i, i1); 
    } 

    @Override 
    public Spliterator<T> spliterator() { 
     return list.spliterator(); 
    } 

    @Override 
    public void addListener(ListChangeListener<? super T> ll) { 
     list.addListener(ll); 
    } 

    @Override 
    public void removeListener(ListChangeListener<? super T> ll) { 
     list.removeListener(ll); 
    } 

    @Override 
    public boolean addAll(T... es) { 
     return list.addAll(es); 
    } 

    @Override 
    public boolean setAll(T... es) { 
     return list.setAll(es); 
    } 

    @Override 
    public boolean setAll(Collection<? extends T> clctn) { 
     return list.setAll(clctn); 
    } 

    @Override 
    public boolean removeAll(T... es) { 
     return list.removeAll(es); 
    } 

    @Override 
    public boolean retainAll(T... es) { 
     return list.retainAll(es); 
    } 

    @Override 
    public void remove(int i, int i1) { 
     list.remove(i, i1); 
    } 

    @Override 
    public FilteredList<T> filtered(Predicate<T> prdct) { 
     return list.filtered(prdct); 
    } 

    @Override 
    public SortedList<T> sorted(Comparator<T> cmprtr) { 
     return list.sorted(cmprtr); 
    } 

    @Override 
    public SortedList<T> sorted() { 
     return list.sorted(); 
    } 

    @Override 
    public void addListener(InvalidationListener il) { 
     list.addListener(il); 
    } 

    @Override 
    public void removeListener(InvalidationListener il) { 
     list.removeListener(il); 
    } 

} 
+0

그러나 리스너 메소드 매개 변수 클래스에서 'change.wasUpdated'를 의미하는 것은 무엇입니까? – user1770962

+0

예, javadoc은 약간 혼란 스럽습니다! 이는 observablelist에 링크 된 항목 (즉, 인덱스 값 0, 1, 2 등)이 업데이트되었는지 (즉, 새 오브젝트가 해당 인덱스에 배치되었는지)를 나타내며 오브젝트 자체는 변경되지 않습니다. 이것의 목적은 ListChangeListener 내에서 List의 ObservableProperty (아마도) ObservableProperty에 ChangeListener를 등록 할 것인지 결정할 수있게하는 것입니다. ObservableList **는 ObservableProperty를 멤버로 요구하지 않으며 Object 유형을 보유 할 수 있습니다. –

+0

그러나 내가하고 싶은 일을 구현할 수 있습니다. 목록의 속성 값이 변경된 경우 콜백을 얻으려면? – user1770962

0

다음 코드는 관측 값으로 관찰 목록에 대한 간단한 구현을 보여줍니다 그래서 :

final ObservableList<MyPOJO> list = new ObservableListWrapper<>(new ArrayList(), o -> { return new Observable[] { 
o.value1Property(), 
o.value2Property(), 
...};}); 

그게 다야! 감사.

+2

API의 기존 기능을 복제하고 있으며 공용 API의 일부가 아닌 클래스도 사용하고 있습니다. –