2
votes

I want to create a ListView with multiple selection only by mouse (without holding down ctrl or shift)

A click on a item should select this item. If there were other items selected, add this new item to selected list. When this item was selected before, deselet it.

I have no idea to keep the selected items. There is a method on selectionModel named "selectIndices()" which only takes one or multiple integers, not a list of integers...

 DataTypesLV.getSelectionModel().selectedItemProperty().addListener(new ChangeListener <String>() {
        @Override
        public void changed(ObservableValue<? extends String> arg0, String oldVal, String newVal) {
            int idx = DataTypesLV.getItems().indexOf(newVal);
            if(newVal.equals(oldVal)){
                DataTypesLV.getSelectionModel().getSelectedIndices().remove(idx);
              } else {
                  DataTypesLV.getSelectionModel().getSelectedIndices().add(idx);
              }
        }
    });

Has anyboy an idea?

1
javafx listview already keeps a list of selected items, so what do you really need hereElltz
I want to multiselect the items only by clicking with mouse.Taros

1 Answers

2
votes

Changing behavior of controls in JavaFX is generally difficult. The "proper" way to do this would be to create a new skin implementation, which involves a lot of work, and since the behavior classes are currently not public API would involve a lot of "reinventing the wheel". A reasonable hack is to intercept the mouse events before they are received by the default skin class, and to modify the selection accordingly. You can do this using an event filter, consuming the event to prevent the skin from receiving it:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;
import javafx.scene.input.MouseEvent;
import javafx.stage.Stage;

public class ListViewModifiedSelection extends Application {

    @Override
    public void start(Stage primaryStage) {
        ListView<String> listView = new ListView<>();
        listView.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);

        for (int i = 1 ; i <= 25 ; i++) {
            listView.getItems().add("Item "+i);
        }

        listView.setCellFactory(lv -> {
            ListCell<String> cell = new ListCell<String>() {
                @Override
                protected void updateItem(String item, boolean empty) {
                    super.updateItem(item, empty);
                    setText(empty ? null : item);
                }
            };

            cell.addEventFilter(MouseEvent.MOUSE_PRESSED, e -> {
                if (cell.isEmpty()) {
                    return ;
                }

                int index = cell.getIndex() ;
                if (listView.getSelectionModel().getSelectedIndices().contains(index)) {
                    listView.getSelectionModel().clearSelection(index);
                } else {
                    listView.getSelectionModel().select(index);
                }

                listView.requestFocus();

                e.consume();
            });

            return cell ;
        });

        primaryStage.setScene(new Scene(listView, 250, 450));
        primaryStage.show();
    }

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

Preventing the skin from receiving the mouse event is a something of a hack, and you do risk breaking existing functionality by doing that. An alternative and more robust solution might be to manage your own "selection" functionality using check boxes in the list cells, or something similar.