Search code examples
javalistviewjavafx-2mvp

JavaFX - move ListView Item - MVC


i am new to this java/javafx stuff and i would be very pleased if somebody could help me out of this.

i have the following code :

// Main.java

package lernen;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class Main extends Application
{
    @Override
    public void start(Stage primaryStage)
    {
        Presenter p = new Presenter();
        View v = new View(p);
        Model m = new Model();

        p.setModel(m);
        p.setView(v);

        Scene scene = new Scene(v.getUI());
        primaryStage.setTitle("Schiebefenster");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

// View.java

package lernen;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.layout.GridPane;

public class View
{
    private Presenter presenter;

    private GridPane pane;

    private Button b1, b2;

    private Label l1, l2;

    // private ObservableList<String> candidates = FXCollections.observableArrayList("a", "b", "c", "d");
    private ObservableList<String> candidates = FXCollections.observableArrayList();

    private ListView<String> list = new ListView<>(candidates);

    private ObservableList<String> selected = FXCollections.observableArrayList();

    private ListView<String> list2 = new ListView<>(selected);

    public View(Presenter presenter)
    {
        this.presenter = presenter;
        initView();
    }

    private void initView()
    {
        pane = new GridPane();
        pane.setVisible(true);
        l1 = new Label("Buchstabe");
        pane.add(l1, 0, 0);
        l2 = new Label("Selektiert");
        pane.add(l2, 4, 0);
        b1 = new Button("<");
        pane.add(b1, 2, 2);
        b2 = new Button(">");
        pane.add(b2, 2, 3);
        // list = new ListView<String>();
        pane.add(list, 0, 1, 2, 5);
        // list2 = new ListView<>();
        pane.add(list2, 4, 1, 2, 5);


        b2.setOnAction((ActionEvent) -> {
            String potential = list.getSelectionModel().getSelectedItem();
            if (potential != null)
            {
                list.getSelectionModel().clearSelection();
                candidates.remove(potential);
                selected.add(potential);
            }
        });

        b1.setOnAction((ActionEvent) -> {
            String selectedItem = list2.getSelectionModel().getSelectedItem();
            if (selectedItem != null)
            {
                list2.getSelectionModel().clearSelection();
                selected.remove(selectedItem);
                candidates.add(selectedItem);
            }
        });

    }

    public GridPane getUI()
    {
        return pane;
    }

    public ListView<String> getList()
    {
        return list;
    }

    public ListView<String> getList2()
    {
        return list2;
    }

}

// Presenter.java

package lernen;

public class Presenter
{
    private View view;

    private Model model;

    public Presenter()
    {
    }

    public void setView(View view)
    {
        this.view = view;
        this.view.getList().setItems(model.getData());
    }

    public void setModel(Model model)
    {
        this.model = model;
    }

}

// Model.java

package lernen;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;

public class Model
{
    private ObservableList<String> data = FXCollections.observableArrayList();

    public Model()
    {
        data.addAll("a", "b", "c", "d", "e");
    }

    public ObservableList<String> getData()
    {
        return data;
    }

}

The given task is to move a item from the left list to the right and vice versa according to the MVP pattern. The problem is: If i move one letter to the right side, the letter on the left side is still there. I hope u guys can help me with this. Thanks in advance!


Solution

  • In the presenter you call

    this.view.getList().setItems(model.getData());
    

    so the list will display the items defined in the ObservableList of the model, therefore, when in the event handler of b2 you call

    candidates.remove(potential);
    

    you actually try to remove the item from a different list (which is actually empty).

    I guess the confusion comes from the following:

    You defined an ObservableList, and you assigned this list to serve as the data model of your ListView.

    private ObservableList<String> candidates = FXCollections.observableArrayList();
    private ListView<String> list = new ListView<>(candidates);
    

    and then when in the controller you called

    this.view.getList().setItems(model.getData());
    

    you thought that, this will fill the candidates ObservableList with the elements of the ObservableList stored in the model (all of them elements from the list in the model will be copied to this list). But, when you call setItems, you set the reference stored in the itemsProperty to be specified ObservableList, therefore it will point to the ObservableList stored in the model and no longer to candidates.

    You can modify this line to:

    this.view.getList().getItems().addAll(model.getData());
    

    The difference is, that in this case getItems() will return the ObservableList stored in the itemsProperty and then all of the elements are copied from the model to this list, but it will still point to candidates. In this case the ObservableList in the model is not touched.

    Or alternatively you can change your listeners as:

    view.getList2().getItems().remove(selectedItem);
    

    which will remove the element from the ObservableList in your model also.

    The situation is basically the same as here: Why does JavaFX table.getItems().clear() clear the ObservableList as well