2012-12-16 13 views
27

Chciałbym wyświetlić listę osób (zakodowanych w POJOS i zawierających nazwę i właściwość nazwy) za pomocą formantu ListView JavaFX. Stworzyłem ListView i dodałem listę osób jako ObservableList. Wszystko działa poprawnie, jeśli usunę lub dodam nową osobę do obiektu ObservableList, ale zmiany w POJO nie powodują aktualizacji ListView. Muszę usunąć i dodać zmodyfikowane POJO z ObservableList, aby wywołać aktualizację ListView. Czy istnieje możliwość wyświetlenia zmian w POJOS bez obejścia opisanego powyżej?JavaFX: Aktualizacja ListView, jeśli element ObservableList zmienia się

+0

czy możesz wysłać swoją klasę POJO? – invariant

+1

Istnieje podejście opisane na forum Oracle [tutaj] (https://forums.oracle.com/thread/2244635), które opisuje rozwiązanie tego problemu. – ThomasMH

+0

Podejście zaproponowane przez kleopatra jest najnowszym i najprostszym rozwiązaniem. Chcę tylko dodać, że 'ekstraktor' może zwrócić wszelkie obserwowalne obiekty, które chce. I że może być bardzo zwięźle napisane z lambdami. – HRJ

Odpowiedz

-1
ObservableList<String> items = FXCollections.observableArrayList(); 
ListView lv; 
lv.setItems(items); 
items.add(); 
items.remove; 
-4

spróbować

list.remove(POJO); 
    list.add(index,POJO); 
+2

nic nowego, prawda? Nawet OP już wspomniał o tym jako o obejściu ... – kleopatra

16

Istnieje kilka aspektów na swoje pytanie (i nie jestem do końca, który jest problem :-) będę zakładać, że jakoś POJO słuchaczy powiadamiania o zmianach, może być pełnoprawnym językiem JavaBean, który jest zgodny z jego umową powiadamiania poprzez wywoływanie zdarzeń propertyChange w razie potrzeby lub w inny sposób - w przeciwnym razie potrzebowałbyś ręcznego wypychania zmiany.

Podstawową metodą uczynienia obiektu FX-ObservableList powiadomieniem własnych odbiorców o mutacjach zawartych elementów jest skonfigurowanie go za pomocą niestandardowego wywołania zwrotnego, które zapewnia szereg obserwowanych obiektów. Jeśli elementy mają FX-właściwości byś zrobił coś takiego:

Callback<Person, Observable[]> extractor = new Callback<Person, Observable[]>() { 

    @Override 
    public Observable[] call(Person p) { 
     return new Observable[] {p.lastNameProperty(), p.firstNameProperty()}; 
    } 
}; 
ObservableList<Person> teamMembers = FXCollections.observableArrayList(extractor); 
// fill list 

Jeśli pojo oznacza pełnoprawnym rdzeń JavaBeans, jego właściwości muszą być dostosowane do FX-właściwościom f.i. za pomocą JavaBeanProperty:

Callback<PersonBean, Observable[]> extractor = new Callback<PersonBean, Observable[]>() { 
    List<Property> properties = new ArrayList<Property>(); 
    @Override 
    public Observable[] call(PersonBean arg0) { 
     JavaBeanObjectProperty lastName = null; 
     JavaBeanObjectProperty age = null; 
     try { 
      lastName = JavaBeanObjectPropertyBuilder.create() 
        .bean(arg0).name("lastName").build(); 
      age = JavaBeanObjectPropertyBuilder.create() 
        .bean(arg0).name("age").build(); 
      // hack around loosing weak references ... 
      properties.add(age); 
      properties.add(lastName); 
     } catch (NoSuchMethodException e) { 
      e.printStackTrace(); 
     } 
     return new Observable[] {lastName, age}; 
    } 

}; 
ObservableList<Person> teamMembers = FXCollections.observableArrayList(extractor); 
// fill list 

Uwaga uwaga: bez zachowania silnego odniesienia do dostosowanych właściwości gdzieś, będą szybko śmieci zgromadzone - pojawiają się, a potem nie mają żadnego wpływu na wszystko (znowu wpadania w pułapkę i znowu nie wiem, czy istnieje dobra strategia, aby tego uniknąć).

W przypadku innych sposobów powiadamiania (możliwie gruboziarnistego) można zaimplementować niestandardową kartę: adapter poniżej nasłuchuje wszystkich zmian właściwości fasoli, a słuchanie innych typów zdarzeń będzie zupełnie analogiczne.

/** 
* Adapt a Pojo to an Observable. 
* Note: extending ObservableValue is too much, but there is no ObservableBase ... 
* 
* @author Jeanette Winzenburg, Berlin 
*/ 
public class PojoAdapter<T> extends ObservableValueBase<T> { 

    private T bean; 
    private PropertyChangeListener pojoListener; 
    public PojoAdapter(T pojo) { 
     this.bean = pojo; 
     installPojoListener(pojo); 
    } 

    /** 
    * Reflectively install a propertyChangeListener for the pojo, if available. 
    * Silently does nothing if it cant. 
    * @param item 
    */ 
    private void installPojoListener(T item) { 
     try { 
      Method method = item.getClass().getMethod("addPropertyChangeListener", 
        PropertyChangeListener.class); 
      method.invoke(item, getPojoListener()); 
     } catch (NoSuchMethodException | SecurityException | IllegalAccessException | 
        IllegalArgumentException | InvocationTargetException e) { 
      e.printStackTrace(); 
     } 
    } 
    /** 
    * Returns the propertyChangeListener to install on each item. 
    * Implemented to call notifyList. 
    * 
    * @return 
    */ 
    private PropertyChangeListener getPojoListener() { 
     if (pojoListener == null) { 
      pojoListener = new PropertyChangeListener() { 

       @Override 
       public void propertyChange(PropertyChangeEvent evt) { 
        fireValueChangedEvent(); 
       } 
      }; 
     } 
     return pojoListener; 
    } 

    @Override 
    public T getValue() { 
     return bean; 
    } 

} 

To Wykorzystanie tak samo jak powyżej (coraz nudne, prawda :-)

Callback<PersonBean, Observable[]> extractor = new Callback<PersonBean, Observable[]>() { 

    @Override 
    public Observable[] call(PersonBean arg0) { 
     return new Observable[] {new PojoAdapter<PersonBean>(arg0)}; 
    } 

}; 
ObservableList<Person> teamMembers = FXCollections.observableArrayList(extractor); 
// fill list 

Niestety, automatyczne aktualizacje ListView z takim chłodnym liście nie będzie działać niezawodnie powodu a bug that's fixed only in jdk8. We wcześniejszych wersjach, jesteś z powrotem na placu 1 - jakoś słuchać zmiany, a następnie ręcznie zaktualizować listę:

protected void notifyList(Object changedItem) { 
    int index = list.indexOf(changedItem); 
    if (index >= 0) { 
     // hack around RT-28397 
     //https://javafx-jira.kenai.com/browse/RT-28397 
     list.set(index, null); 
     // good enough since jdk7u40 and jdk8 
     list.set(index, changedItem); 
    } 
} 
+0

Dziękuję bardzo za zastrzeżenie, gdzie o tym przeczytałeś? Czy mógłbyś to udostępnić tutaj :) – TuanAnh207

5

Można ręcznie wyzwolić ListView.EditEvent — co spowoduje ListView zaktualizować — przez wywołanie metody ListView::fireEvent odziedziczone po javafx.scene.Node. Na przykład,

/** 
* Informs the ListView that one of its items has been modified. 
* 
* @param listView The ListView to trigger. 
* @param newValue The new value of the list item that changed. 
* @param i The index of the list item that changed. 
*/ 
public static <T> void triggerUpdate(ListView<T> listView, T newValue, int i) { 
    EventType<? extends ListView.EditEvent<T>> type = ListView.editCommitEvent(); 
    Event event = new ListView.EditEvent<>(listView, type, newValue, i); 
    listView.fireEvent(event); 
} 

lub jako jedną wkładką,

listView.fireEvent(new ListView.EditEvent<>(listView, ListView.editCommitEvent(), newValue, i)); 

Oto przykładowa aplikacja wykazać jego wykorzystanie.

/** 
* An example of triggering a JavaFX ListView when an item is modified. 
* 
* Displays a list of strings. It iterates through the strings adding 
* exclamation marks with 2 second pauses in between. Each modification is 
* accompanied by firing an event to indicate to the ListView that the value 
* has been modified. 
* 
* @author Mark Fashing 
*/ 
public class ListViewTest extends Application { 

    /** 
    * Informs the ListView that one of its items has been modified. 
    * 
    * @param listView The ListView to trigger. 
    * @param newValue The new value of the list item that changed. 
    * @param i The index of the list item that changed. 
    */  
    public static <T> void triggerUpdate(ListView<T> listView, T newValue, int i) { 
     EventType<? extends ListView.EditEvent<T>> type = ListView.editCommitEvent(); 
     Event event = new ListView.EditEvent<>(listView, type, newValue, i); 
     listView.fireEvent(event); 
    } 

    @Override 
    public void start(Stage primaryStage) { 
     // Create a list of mutable data. StringBuffer works nicely. 
     final List<StringBuffer> listData = Stream.of("Fee", "Fi", "Fo", "Fum") 
       .map(StringBuffer::new) 
       .collect(Collectors.toList()); 
     final ListView<StringBuffer> listView = new ListView<>(); 
     listView.getItems().addAll(listData); 
     final StackPane root = new StackPane(); 
     root.getChildren().add(listView); 
     primaryStage.setScene(new Scene(root)); 
     primaryStage.show(); 
     // Modify an item in the list every 2 seconds. 
     new Thread(() -> { 
      IntStream.range(0, listData.size()).forEach(i -> { 
       try { 
        Thread.sleep(2000); 
       } catch (InterruptedException e) { 
        e.printStackTrace(); 
       } 
       System.out.println(listData.get(i)); 
       Platform.runLater(() -> { 
        // Where the magic happens. 
        listData.get(i).append("!"); 
        triggerUpdate(listView, listData.get(i), i); 
       });    
      }); 
     }).start(); 
    } 

    public static void main(String[] args) { 
     launch(args); 
    } 

} 
1

Powinieneś zająć listę obserwowalną i zaktualizować obiekt używając list.set (selectedIndex, object); Mój przykład pokazuje przycisk z metodą uchwytu. W ten edytowany lista użytkowników w fx viewtable

Button commit = new Button("Commit"); 
    commit.setOnAction(new EventHandler<ActionEvent>() { 
     public void handle(ActionEvent evt) { 
      int selectedIndex = tableView.getSelectionModel().getSelectedIndex(); 
      User user = tableView.getSelectionModel().getSelectedItem(); 
      user.setId(Integer.parseInt(idTF.getText())); 
      user.setName(nameCB.getValue()); 
      user.setSurname(srnameTF.getText()); 
      user.setAddress(addressTF.getText()); 
      service.getUsers().set(selectedIndex, user); 
      tableView.toFront(); 
     } 
    }); 
3

Korzystanie Francis pomysł robiłam:

list.set(list.indexOf(POJO), POJO); 

nie może być najlepszym rozwiązaniem, ale działało.

2

Ponieważ Java 8u60 ListView oficjalnie obsługuje metodę refresh() w celu ręcznej aktualizacji widoku. JavaDoc:

Jest to przydatne w przypadkach, gdy podstawowe źródło danych uległo zmianie w sposób, który nie jest obserwowany przez samą ListView.

Pomyślnie użyłem tej metody dla tego wydania, aby zaktualizować zawartość elementów w ListView.

Powiązane problemy