从 ListView 中获取选定的项目 |复选框列表单元格

Getting selected Items from a ListView | CheckBoxListCell

我想为每个项目创建一个简单的 ListViewCheckBox。这已经完成了。现在我正在寻找一种方法来从此 ListView 中获取所有选定的项目。

我发现我可以使用方法 setCellFactory() 在单独的集合中选择项目时添加项目,并在未选择项目时删除项目。但我认为这是一种丑陋的方式。

ListView<String> listView = new ListView<>();
    String[] toppings = {"Cheese", "Pepperoni", "Black Olives"};
    listView.getItems().addAll(toppings);
    listView.setCellFactory(CheckBoxListCell.forListView(new Callback<String, ObservableValue<Boolean>>() {
        @Override
        public ObservableValue<Boolean> call(String item) {
        BooleanProperty observable = new SimpleBooleanProperty();
        observable.addListener((obs, wasSelected, isNowSelected)
            -> System.out.println("Check box for " + item + " changed from " + wasSelected + " to " + isNowSelected)
        );
        return observable;
        }
}));

如何从 ListView 中获取所选项目的列表?

如果您向模型 class 添加布尔值 属性,例如:

public class Topping {

    private final String name ;

    private final BooleanProperty selected = new SimpleBooleanProperty();

    public BooleanProperty selectedProperty() {
        return selected ;
    }

    public final boolean isSelected() {
        return selectedProperty().get();
    }

    public final void setSelected(boolean selected) {
        selectedProperty().set(selected);
    }

    public Topping(String name) {
        this.name = name ;
    }

    public String getName() {
        return name ;
    }

    @Override
    public String toString() {
        return getName(); 
    }
}

并在复选框列表单元格中使用它:

listView.setCellFactory(CheckBoxListCell.forListView(Topping::selectedProperty));

然后您可以创建一个包含所有选定浇头的过滤列表:

ListView<Topping> listView = new ListView<>();
listView.getItems().addAll(new Topping("Cheese"), new Topping("Pepperoni"),
    new Topping("Black Olives"));

ObservableList<Topping> selectedToppings = 
    listView.getItems().filtered(Topping::isSelected);

现在 selectedToppings 将始终准确包含在列表视图中选中复选框的浇头。

这样使用

public void start(Stage primaryStage) {
        try {
            ListView<String> listView = new ListView<>();
            Button button = new Button("Get");
            List<String> list = new ArrayList<>();
            String[] toppings = { "Cheese", "Pepperoni", "Black Olives" };
            listView.getItems().addAll(toppings);
            listView.setCellFactory(CheckBoxListCell.forListView(new Callback<String, ObservableValue<Boolean>>() {
                @Override
                public ObservableValue<Boolean> call(String item) {
                    BooleanProperty observable = new SimpleBooleanProperty();
                    observable.addListener((obs, wasSelected, isNowSelected) -> {
                        if (isNowSelected) {
                            list.add(item);
                        } else {
                            list.remove(item);
                        }
                    });
                    return observable;
                }
            }));

            button.setOnAction(e -> {
                for (int i = 0; i < list.size(); i++) {
                    System.out.println(list.get(i));
                }
            });

            VBox root = new VBox();
            root.setAlignment(Pos.CENTER);
            root.getChildren().addAll(listView, button);
            Scene scene = new Scene(root, 300, 250);
            primaryStage.setScene(scene);
            primaryStage.show();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

除非 selected 属性 是您模型的一部分,否则您现在将用户交互需求与您的模型混合在一起,我会尽量避免这种情况。

例如,如果您要创建两个列表,并在其中将项目从一个列表移到另一个列表,您是否仍会使用此 属性 或包含在该列表中就足够了?

在您的(比萨饼?)示例中,一旦向用户确认的订单应仅包含已选择的浇头,如果您的比萨饼订单包含所有可能的浇头并且厨房必须迭代,这将是一个奇怪的模型在列表中找到所需的浇头。这强烈表明它被选中的事实不是模型的一部分,而是用户交互项。

为避免混淆用户界面和模型,您可以使用 CheckListView of the ControlsFX 库,它有一个 CheckModel,它不仅允许您查询选中的项目,还可以在您的逻辑需要时操作它这样做。