Search code examples
listviewjavafxobservablelist

JavaFX ListView isn't updated when ObservableList changes on JRE6 JavaFX2.2


My problem is that my ListView (checkbox list view) does not update when the ObservableList changes.

This problem only happens when I use a dated version of JavaFX and a dated JRE (jre6 and jfx2.2). When I use jre8 and the JavaFX that it includes, the problem is gone and the list refreshes nicely.

Working source code on latest JavaFX described above:

The list:

public ObservableList<TestCaseCheckboxModel> TestCases;

//Constructor
public SessionTestCaseModel() {
    TestCases = FXCollections.observableArrayList(new Callback<TestCaseCheckboxModel, Observable[]>(){
        @Override
        public Observable[] call(TestCaseCheckboxModel model) {
            return new Observable[]{model.TestCaseName, model.TestCaseStatus, model.TestCaseSelected};//selected might not be needed since it's bound to the listview anyways
        }
    });
}

The Class the list uses has these properties:

public StringProperty TestCaseName = new SimpleStringProperty();
public IntegerProperty TestCaseStatus = new SimpleIntegerProperty();
public BooleanProperty TestCaseSelected = new SimpleBooleanProperty();

The List is populated externally.

The ListView:

@FXML
private ListView<TestCaseCheckboxModel> testcaseListView;

//bind the checkbox select to the model property
testcaseListView.setCellFactory(CheckBoxListCell.forListView(new Callback<TestCaseCheckboxModel, ObservableValue<Boolean>>() {
   @Override
   public ObservableValue<Boolean> call(TestCaseCheckboxModel item) {
        return item.TestCaseSelected;
    }
}));

//initiate the checkbox list view with the model items
testcaseListView.setItems(getModel().TestCases);

The problem is that when I change the List items, the ListView won't change on the old JavaFX version.

It might be worth mentioning that after I add/remove an item from the list, the items that are in the list update (like they are supposed to when they themselves change). But, as far as I know, calling the add function of the list triggers the ListView update, no matter what.

On the other hand, when the item properties change, this will trigger, with both the old and new JavaFX versions, so the Callback extractor is working as intended:

getModel().TestCases.addListener(new ListChangeListener<TestCaseCheckboxModel>(){
         @Override
            public void onChanged(Change change) {
                System.out.println(change);
        }
    });

Is there a known workaround for this?

Thank you for your help.


Solution

  • Answering my own question with help from this thread: https://stackoverflow.com/a/25962110/4073727

    I implemented a custom ListViewSkin that is capable of updating the ListView "from the inside":

    public class UpdateableListViewSkin<T> extends ListViewSkin<T> {
    
        public UpdateableListViewSkin(ListView<T> arg0) {
            super(arg0);
        }
    
        public void refresh() {
            super.flow.recreateCells();
        }
    
    }
    

    Then I add the ObservableList to the ListView, instantiate the Skin and set it to the ListView.

    //initiate the checkbox list view with the model items
    testcaseListView.setItems(getModel().TestCases);
    
    UpdateableListViewSkin<TestCaseCheckboxModel> skin = new UpdateableListViewSkin<TestCaseCheckboxModel>(testcaseListView);
    testcaseListView.setSkin(skin);
    

    The key is that you need to add a working onChange listener to the ObservableList, which will trigger the Skin's .refresh() method. I did this right after I set the Skin to the ListView:

    getModel().TestCases.addListener(new ListChangeListener<TestCaseCheckboxModel>(){
        @SuppressWarnings("unchecked")
        @Override
            public void onChanged(Change change) {
                ((UpdateableListViewSkin<TestCaseCheckboxModel>)testcaseListView.getSkin()).refresh();
            }
        });
    

    This workaround triggers the ListView's update functions, like it does on the new version of JavaFX.