JavaFX 2.2: How to force a redraw/update of a ListView

后端 未结 5 1113
悲&欢浪女
悲&欢浪女 2021-02-08 19:16

I have a ListView control in a JavaFX 2 modal dialog window.

This ListView displays DXAlias instances, the ListCells for which are manufactured by a cell factory. The m

相关标签:
5条回答
  • 2021-02-08 19:25

    Seems, you don't need to use updateItem method. What you need - is to store indicator of currently default cell (which is in user data now), into OblectProperty. And add a listener on this property from each cell. When value changes, reassign a style.

    But I think, such binding will call another problem - while scrolling, new cells will be created, but the old ones will not leave the binding, which can cause memory leak. So you need to add listener on cell removing from the scene. I think, it can be done by adding a listener on the parentProperty, when it becomes null - remove binding.

    UI shouldn't be forced to be updated. It is updated automaticly, when properties/rendering of existing nodes is changed. So you just need to update the appearance/property of existing nodes (cells). And not to forget, that cells can be created massively, during scrolling/rerendering, etc.

    0 讨论(0)
  • 2021-02-08 19:33

    For now, I am able to get the ListView to redraw and correctly indicate the selected default by using the following method, called forceListRefreshOn(), in my button handler:

    @FXML
    void handleAliasDefault(ActionEvent event) {
    
        int sel = lsvAlias.getSelectionModel().getSelectedIndex();
        if (sel >= 0 && sel < lsvAlias.getItems().size()) {
            lsvAlias.setUserData(lsvAlias.getItems().get(sel));
            this.<DXSynonym>forceListRefreshOn(lsvAlias);
        }
    }
    

    The helper method just swaps out the ObservableList from the ListView and then swaps it back in, presumably forcing the ListView to update its ListCells:

    private <T> void forceListRefreshOn(ListView<T> lsv) {
        ObservableList<T> items = lsv.<T>getItems();
        lsv.<T>setItems(null);
        lsv.<T>setItems(items);
    }
    
    0 讨论(0)
  • 2021-02-08 19:37

    I am not sure if I am missing something, but at first I tried scottb's solution, but then I found out that there is an already implemented refresh() method, which did the trick for me.

    ListView<T> list;
    list.refresh();
    
    0 讨论(0)
  • 2021-02-08 19:39

    For any body else that ends up on this page:

    The correct way of doing this is to supply your observable list with an "extractor" Callback. This will signal the list (and ListView) of any property changes.

    Custom class to display:

    public class Custom {
        StringProperty name = new SimpleStringProperty();
        IntegerProperty id = new SimpleIntegerProperty();
    
        public static Callback<Custom, Observable[]> extractor() {
            return new Callback<Custom, Observable[]>() {
                @Override
                public Observable[] call(Custom param) {
                    return new Observable[]{param.id, param.name};
                }
            };
        }
    
        @Override
        public String toString() {
            return String.format("%s: %s", name.get(), id.get());
        }
    }
    

    And your main code body:

    ListView<Custom> myListView;
    //...init the ListView appropriately
    ObservableList<Custom> items = FXCollections.observableArrayList(Custom.extractor());
    myListView.setItems(items);
    Custom item = new Custom();
    items.add(item);
    item.name.set("Mickey Mouse");
    // ^ Should update your ListView!!!
    

    See (and similar methods): https://docs.oracle.com/javafx/2/api/javafx/collections/FXCollections.html#observableArrayList(javafx.util.Callback)

    0 讨论(0)
  • 2021-02-08 19:39

    Not sure if this works in JavaFX 2.2, but it does in JavaFX 8 and took me a while to figure out. You need to create your own ListViewSkin and add a refresh method like:

    public void refresh() {
        super.flow.recreateCells(); 
    }
    

    This will call updateItem without having to replace the whole Observable collection.

    Also, to use the new Skin, you need to instantiate it and set it on the initialize method of the controller in case you're using FXML:

    MySkin<Subscription> skin = new MySkin<>(this.listView); // Injected by FXML
    this.listView.setSkin(skin);
    ...
    ((MySkin) listView.getSkin()).refresh(); // This is how you use it    
    

    I found this after debugging the behavior of an Accordion. This controls refresh the ListView it contains everytime you expand it.

    0 讨论(0)
提交回复
热议问题