Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to move items between two ListViews with change listener JavaFX

I have two ListViews, allStudentsList has items already populated within it, currentStudentList has none. My aim for when the user selects an item in allStudentList is for that item be moved into currentStudentList. I have done this by placing a listener on the selection model of the allStudentList.

I get an IndexOutOfBoundsException and I am not sure why this is occurring. From testing, it seems that this problem is isolated to the last 4 lines of this method yet I am not sure why.

allStudentsList.getSelectionModel().selectedItemProperty()
        .addListener((observableValue, oldValue, newValue) -> {
            if (allStudentsList.getSelectionModel().getSelectedItem() != null) {

                ArrayList<String> tempCurrent = new ArrayList<>();
                for (String s : currentStudentList.getItems()) {
                    tempCurrent.add(s);
                }

                ArrayList<String> tempAll = new ArrayList<>();
                for (String s : allStudentsList.getItems()) {
                    tempAll.add(s);
                }

                tempAll.remove(newValue);
                tempCurrent.add(newValue);

                // clears current studentlist and adds the new list
                if (currentStudentList.getItems().size() != 0) {
                    currentStudentList.getItems().clear();
                }
                currentStudentList.getItems().addAll(tempCurrent);

                // clears the allStudentList and adds the new list
                if (allStudentsList.getItems().size() != 0) {
                    allStudentsList.getItems().clear();
                }
                allStudentsList.getItems().addAll(tempAll);
            }
        });
like image 287
Philayyy Avatar asked Sep 27 '16 01:09

Philayyy


1 Answers

As a quick fix you can wrap the code parts that modify the item lists into a Platform.runLater(...) block:

Platform.runLater(() -> {
    // clears current studentlist and adds the new list
    if (currentStudentList.getItems().size() != 0) 
        currentStudentList.getItems().clear();

    currentStudentList.getItems().addAll(tempCurrent);
});

Platform.runLater(() -> {
    // clears the allStudentList and adds the new list
    if (allStudentsList.getItems().size() != 0) 
        allStudentsList.getItems().clear();

    allStudentsList.getItems().addAll(tempAll);
});

The problem is that you cannot change the selection while a selection change is being processed. As you remove all elements with allStudentsList.getItems().clear();, the selection will change (the selected index will be -1), the mentioned condition will met. This is what the usage of the Platform.runLater(...) block will prevent by "postponing" the modification.

But your whole handler could be exchanged with

allStudentsList.getSelectionModel().selectedItemProperty().addListener((obs, oldValue, newValue) -> {
    if (newValue != null) {

        Platform.runLater(() -> {
            allStudentsList.getSelectionModel().select(-1);
            currentStudentList.getItems().add(newValue);
            allStudentsList.getItems().remove(newValue);
        });
    }
});

It sets the selected index to -1: nothing is selected in the ListView to avoid changing to a different item when removing the current one (this was done implicitly in your version by clearing the list), then it adds the currently selected element to the s"elected list", then it removes the current element from the "all item list". All of these actions are wrapped in the mentioned Platform.runLater(...) block.

like image 51
DVarga Avatar answered Oct 24 '22 21:10

DVarga