Eduard Nickel
Eduard Nickel

Reputation: 155

How to refresh ListView in JavaFX

Sorry for bothering with such wide explained topic. I have been creating dialog to pick string values from one list to another. There is list of options at the start. get/setSourceItems and get/setTargetItems are getters and setters for ObservableLists

private ListView<String> sourceListView;
private ObservableList<String> sourceItems;
private ListView<String> targetListView;
private ObservableList<String> targetItems;

        List<String> options = getSelectedMetaDataList();
        setSourceItems(FXCollections.observableArrayList(options));
        sourceListView = new ListView(getSourceItems());
        setTargetItems(FXCollections.observableArrayList(new ArrayList()));
        targetListView = new ListView(getTargetItems());

Dialog shows listViews well at the start. Source is filled with options and target is empty. But when I use button then nothing happens on UI. For example when I move all items from source to target I can see items in targetItems observableList but nothing in targetListView on UI.

        Button selectAllBtn = new Button(">>");
            selectAllBtn.setOnAction(new EventHandler<ActionEvent>() {
                @Override
                public void handle(ActionEvent event) {
                    if (getSourceItems().size() > 0) {
                        setTargetItems(getSourceItems());
                        targetListView.refresh();
                        setSourceItems(null);
                        sourceListView.refresh();
                        System.out.println("selectAllBtn target: " + getTargetItems().toString());
                    }
                }
            });

Something wrong is with selection model too. Because when I try move selected items sourceListView.getSelectionModel().getSelectedItems() is empty even when I have item highlighted. I tried to move items not only by observableList but using targetListView.getItems().addAll(movingItems); Still no changes on UI.

        Button selectOneBtn = new Button(">");
            selectOneBtn.setOnAction(new EventHandler<ActionEvent>() {
                @Override
                public void handle(ActionEvent event) {
                    ObservableList<String> movingItems = sourceListView.getSelectionModel().getSelectedItems();
                    System.out.println("selectOneBtn: " + sourceListView.getSelectionModel().getSelectedItems().size());
                    System.out.println("selectOneBtn: " + targetListView.getSelectionModel().getSelectedItems().size());
                    if (movingItems.size() > 0) {
                    System.out.println("selectOneBtn: " + movingItems.toString());
                    sourceListView.getSelectionModel().clearSelection();
                    sourceListView.getItems().removeAll(movingItems);
                    sourceListView.refresh();
                    targetListView.getItems().addAll(movingItems);
                    targetListView.refresh();
                    System.out.println("selectOneBtn source: " + getSourceItems().toString());
                    System.out.println("selectOneBtn target: " + getTargetItems().toString());
                    }
                }
            });

Thanks in advance.

Upvotes: 0

Views: 26564

Answers (2)

SedJ601
SedJ601

Reputation: 13859

I am guessing you are trying to do something similar to this.

@FXML ListView lv1, lv2;

ArrayList options = new ArrayList();
ObservableList<String> items;

ObservableList<String> items2;


//This is used to get the selected item from lv1 and add it to lv2
@FXML
private void handleButtonAction(ActionEvent event) {
    items2.add(lv1.getSelectionModel().getSelectedItem().toString());             
}

//This is used to get all items from lv1 and add them to lv2
@FXML 
private void handleGetAllButtonAction(ActionEvent event)
{
    for(String entry : items)
    {
       items2.add(entry);
    }
}

@Override
public void initialize(URL url, ResourceBundle rb) {
    // TODO

   options.add("hello");
   options.add("bye");
   options.add("hola");
   items = FXCollections.observableArrayList(options);
   items2 = FXCollections.observableArrayList();

   System.out.println(items.size());
   lv1.setItems(items);
   lv2.setItems(items2);

}    

Upvotes: 0

James_D
James_D

Reputation: 209418

You change the underlying observable list, but you never tell the list view to use the new list:

Button selectAllBtn = new Button(">>");

selectAllBtn.setOnAction(new EventHandler<ActionEvent>() {
    @Override
    public void handle(ActionEvent event) {
        if (getSourceItems().size() > 0) {
            setTargetItems(getSourceItems());

            targetListView.setItems(getTargetItems());

            // use an empty list, not null:
            setSourceItems(FXCollections.observableArrayList());

            sourceListView.setItems(getSourceItems());

            System.out.println("selectAllBtn target: " + getTargetItems().toString());
        }
    }
});

Alternatively, instead of changing to a new list, just change the contents of the existing list. Since the lists are observable, the listviews will see the changes automatically:

Button selectAllBtn = new Button(">>");

selectAllBtn.setOnAction(new EventHandler<ActionEvent>() {
    @Override
    public void handle(ActionEvent event) {
        if (getSourceItems().size() > 0) {

            getTargetItems().setAll(getSourceItems());

            getSourceItems().clear();

            System.out.println("selectAllBtn target: " + getTargetItems().toString());
        }
    }
});

For your selection problem, you get a reference to the list of selected items:

 ObservableList<String> movingItems = sourceListView.getSelectionModel().getSelectedItems();

and then you clear the selection:

 sourceListView.getSelectionModel().clearSelection();

which will, of course, make the list of selected items empty.

So now when you do

 sourceListView.getItems().removeAll(movingItems);
 targetListView.getItems().addAll(movingItems);

you are passing in an empty list each time, and so nothing will change.

Instead, copy the selected items:

List<String> movingItems = new ArrayList<>(sourceListView.getSelectionModel().getSelectedItems());

and then

sourceListView.getItems().removeAll(movingItems);
targetListView.getItems().addAll(movingItems);

will have the desired effect (no need to call refresh()).

Here is a SSCCE (note I unified both behaviors into a single method, since they are essentially the same):

import java.util.ArrayList;
import java.util.List;

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ListView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class MoveItemsBetweenListViews extends Application {

    private ObservableList<String> sourceItems;
    private ObservableList<String> targetItems;



    @Override
    public void start(Stage primaryStage) {
        sourceItems = FXCollections.observableArrayList("One", "Two", "Three", "Four");
        targetItems = FXCollections.observableArrayList();

        ListView<String> sourceListView = new ListView<>(sourceItems);
        ListView<String> targetListView = new ListView<>(targetItems);

        Button moveAllButton = new Button(">>");
        Button moveSelectedButton = new Button(">");

        moveAllButton.setOnAction(e -> moveItems(sourceItems));
        moveSelectedButton.setOnAction(e -> moveItems(sourceListView.getSelectionModel().getSelectedItems()));

        VBox buttons = new VBox(10, moveAllButton, moveSelectedButton);
        buttons.setAlignment(Pos.CENTER);

        BorderPane root = new BorderPane(buttons, null, targetListView, null, sourceListView);
        Scene scene = new Scene(root, 600, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    private void moveItems(List<String> itemsToMove) {
        List<String> movingItems = new ArrayList<>(itemsToMove);
        sourceItems.removeAll(movingItems);
        targetItems.addAll(movingItems);
    }

    public static void main(String[] args) {
        launch(args);
    }
}

Upvotes: 3

Related Questions