2012-12-16 24 views
27

Tôi muốn hiển thị danh sách những người (được mã hóa trong POJOS và có chứa tên và họ) bằng cách sử dụng điều khiển ListView của JavaFX. Tôi đã tạo ListView và thêm danh sách những người như là một ObservableList. Mọi thứ hoạt động tốt nếu tôi xóa hoặc thêm một người mới vào ObservableList, nhưng những thay đổi trong POJO không kích hoạt cập nhật của ListView. Tôi phải xóa và thêm POJO đã sửa đổi từ ObservableList để kích hoạt cập nhật của ListView. Có khả năng hiển thị các thay đổi trong POJOS mà không có giải pháp được mô tả ở trên không?JavaFX: Cập nhật ListView nếu một phần tử của ObservableList thay đổi

+0

bạn có thể đăng lớp POJO u r không? – invariant

+1

Có một cách tiếp cận được nêu tại Diễn đàn Oracle [ở đây] (https://forums.oracle.com/thread/2244635) mà phác thảo một giải pháp cho vấn đề này. – ThomasMH

+0

Cách tiếp cận được đưa ra bởi kleopatra là giải pháp mới nhất và đơn giản nhất. Tôi muốn chỉ thêm rằng 'extractor' có thể trả về bất kỳ quan sát nào mà nó muốn. Và nó có thể được viết gọn gàng với lambdas. – HRJ

Trả lời

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

thử này

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

không có gì mới, phải không? Ngay cả OP đã đề cập đến điều này như một cách giải quyết ... – kleopatra

16

Có một số khía cạnh cho câu hỏi của bạn (và tôi không hoàn toàn là vấn đề :-) Tôi sẽ giả định rằng POJO của bạn bằng cách nào đó thông báo cho người nghe về những thay đổi, có thể là một JavaBean chính thức, tuân thủ hợp đồng thông báo của nó thông qua việc kích hoạt các sự kiện propertyChange khi cần hoặc một số phương tiện khác - nếu không, bạn sẽ cần một số thay đổi thủ công của thay đổi.

Cách tiếp cận cơ bản để tạo FX-ObservableList thông báo cho người nghe của riêng mình về đột biến của các phần tử được chứa là cấu hình nó với một cuộc gọi lại tùy chỉnh cung cấp một mảng các Observables. Nếu các yếu tố có fx-thuộc tính bạn sẽ làm điều gì đó như:

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 

Nếu POJO là-một JavaBean lõi full-fledged, tính chất của nó phải được thích nghi với fx-tài sản, f.i. bằng cách sử dụng 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 

Lưu ý một caveat: không giữ một tham chiếu mạnh để các thuộc tính thích nghi nơi nào đó, họ sẽ nhanh chóng thu gom rác - và sau đó dường như không có hiệu lực ở tất cả (rơi vào bẫy một lần nữa và một lần nữa, không chắc chắn làm thế nào nếu có một chiến lược tốt để tránh nó).

Đối với bất kỳ phương tiện thông báo nào khác (có thể là hạt thô), bạn có thể triển khai bộ điều hợp tùy chỉnh: bộ điều hợp bên dưới nghe tất cả các thuộc tính của bean, nghe các loại sự kiện khác tương tự.

/** 
* 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; 
    } 

} 

Đó là việc sử dụng chỉ là tương tự như trên (nhận được nhàm chán, không phải là nó :-)

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 

Thật không may, cập nhật tự động của một ListView với danh sách mát mẻ như vậy sẽ không làm việc đáng tin cậy do a bug that's fixed only in jdk8. Trong các phiên bản trước đó, bạn đang trở lại tại quảng trường 1 - bằng cách nào đó lắng nghe với sự thay đổi và sau đó tự cập nhật danh sách:

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

Cảm ơn bạn rất nhiều vì đã báo trước, bạn đã đọc về nó ở đâu? Bạn vui lòng chia sẻ nó ở đây :) – TuanAnh207

5

Bạn có thể kích hoạt một ListView.EditEvent — mà sẽ gây ra ListView để cập nhật — bằng cách gọi phương pháp ListView::fireEvent được kế thừa từ javafx.scene.Node. Ví dụ,

/** 
* 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); 
} 

Hoặc như một liner,

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

Đây là một ứng dụng mẫu để chứng minh việc sử dụng nó.

/** 
* 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

Bạn nên lấy danh sách quan sát và cập nhật đối tượng bằng cách sử dụng list.set (selectedIndex, đối tượng); Ví dụ của tôi hiển thị nút với phương pháp xử lý. Trong này tôi thay đổi nội dung người dùng danh sách trong 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

Sử dụng Francis ý tưởng tôi đã làm:

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

không thể là giải pháp tốt nhất nhưng làm việc.

2

Vì Java 8u60 ListView chính thức hỗ trợ phương thức refresh() để cập nhật chế độ xem theo cách thủ công. JavaDoc:

Điều này rất hữu ích trong trường hợp nguồn dữ liệu cơ bản đã thay đổi theo cách không được quan sát bởi chính ListView.

Tôi đã sử dụng thành công phương pháp này cho vấn đề này tại đây để cập nhật nội dung của các mục trong ListView.

Các vấn đề liên quan