0

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.

nmarton.t
  • 15
  • 5
  • Could you post an example code modifying the data? Furthermore, **if** you make those property fields `public`, you should also add the `final` modifier, just to be sure the properties are not replaced. – fabian Aug 17 '16 at 13:11
  • Thank you for the suggestion, I will definitely include safety measures, but I rather wanted to get this problem out of my way first. An example could simply be `getModel().TestCases.get(0).TestCaseName.set("New name")`. Like this, any of the properties can be modified from the `TestCases` list. So there is no special trick for that. – nmarton.t Aug 17 '16 at 13:54
  • And make sure to check the answer that I posted that solves the issue, if you were wondering if the error was in modifying the data. It was not, and I found the solution as well (answer below). I will accept it as soon as I can (day after tomorrow) so it will be obvious. – nmarton.t Aug 17 '16 at 13:59

1 Answers1

1

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.

Community
  • 1
  • 1
nmarton.t
  • 15
  • 5
  • The problem you might run into here is that in Java 9, `ListViewSkin` will change package, so this workaround will fail to even run in that release. Do you really still need to support Java 6? – James_D Aug 17 '16 at 16:37
  • Yes, I absolutely do, the application will have to run on dated JVMs (old MATLAB versions use jre6 so that is my requirement). Thank you for the heads up, though, I will keep that in mind and find a fix for it when the problem arises. – nmarton.t Aug 18 '16 at 06:52