Java 为自定义组合框创建侦听器逻辑

Java 为自定义组合框创建侦听器逻辑,java,javafx,Java,Javafx,我正在尝试为我创建的自定义组合框创建侦听器逻辑,该组合框包含带有复选框的项 我无法继续,因为我不知道如何做 MainApplication.java public class MainApplication extends Application{ @Override public void start(Stage stage) { Scene scene = new Scene(new VBox(), 450, 250); ComboBox<

我正在尝试为我创建的自定义组合框创建侦听器逻辑,该组合框包含带有复选框的项

我无法继续,因为我不知道如何做

MainApplication.java

public class MainApplication extends Application{

 @Override
    public void start(Stage stage) {
        Scene scene = new Scene(new VBox(), 450, 250);

        ComboBox<ComboBoxItemWrap<Person>> cb = new ComboBox<>();

        @SuppressWarnings("unchecked")
        ObservableList<ComboBoxItemWrap<Person>> options = FXCollections.observableArrayList(
                new ComboBoxItemWrap<>(new Person("A", "12-Aug-1994")),
                new ComboBoxItemWrap<>(new Person("B", "13-Aug-1994")),
                new ComboBoxItemWrap<>(new Person("C", "14-Aug-1994"))
                );

        cb.setCellFactory( c -> {
            ListCell<ComboBoxItemWrap<Person>> cell = new ListCell<ComboBoxItemWrap<Person>>(){
                @Override
                protected void updateItem(ComboBoxItemWrap<Person> item, boolean empty) {
                    super.updateItem(item, empty);
                    if (!empty) {
                        final CheckBox cb = new CheckBox(item.toString());
                        cb.selectedProperty().bind(item.checkProperty());
                        setGraphic(cb);
                    }
                }
            };

            cell.addEventFilter(MouseEvent.MOUSE_RELEASED, event -> {
                cell.getItem().checkProperty().set(!cell.getItem().checkProperty().get());
                StringBuilder sb = new StringBuilder();
                cb.getItems().filtered( f-> f!=null).filtered( f-> f.getCheck()).forEach( p -> {
                    sb.append("; "+p.getItem());
                });
                final String string = sb.toString();
                cb.setPromptText(string.substring(Integer.min(2, string.length())));
            });

            return cell;
        });

        cb.setItems(options);

        VBox root = (VBox) scene.getRoot();

        Button bt = new Button("test");

        bt.setOnAction(event -> {
            cb.getItems().filtered( f -> f.getCheck()).forEach( item -> System.out.println(item.getItem()));
        });

        root.getChildren().addAll(cb, bt);
        stage.setScene(scene);
        stage.show();
 }

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

}

在输出应用程序中,将填充带有复选框的项目列表。在列表中选择任意数量的条目时,条目名称将填充在组合框本身上,并用“;”分隔。现在,我希望我的后端代码能够监听并识别已选择的条目,以便执行进一步的操作。

您可能不需要重新发明控制盘。考虑使用.< /P> 尽管如此,代码中仍存在一些问题:

您永远不会在选中复选框时更新属性。这可以通过使用双向绑定轻松解决。 由于组合框弹出窗口已关闭,因此在触发鼠标释放事件时,复选框不再启用。这是复选框的选定状态的预请求站点。修改皮肤可以改变这种行为。 您可以使用ObservableList.filtered创建FilteredList,然后立即将其丢弃。您还可以在鼠标释放的事件筛选器中创建筛选列表的筛选列表。这本身并没有错,但您在那里创建了一个昂贵的对象,而不需要这样做:只需在那里获得一个流。如果结果只需要一次,这是一种更轻量级的列表过滤方法。仅当您需要包含另一个ObservableList中的元素并自动更新的ObservableList时,才使用filtered/FilteredList。 还请注意,有一种方法可以对属性的更改进行ObservableList触发器更新更改:使用observableArrayList方法,将提取器作为参数

这就是如何重写代码以使其正常工作:

VBox root = new VBox();
Scene scene = new Scene(root, 450, 250);

ComboBox<ComboBoxItemWrap<Person>> cb = new ComboBox<>();

ObservableList<ComboBoxItemWrap<Person>> options = FXCollections.observableArrayList(item -> new Observable[] {item.checkProperty()});
options.addAll(
        new ComboBoxItemWrap<>(new Person("A", "12-Aug-1994")),
        new ComboBoxItemWrap<>(new Person("B", "13-Aug-1994")),
        new ComboBoxItemWrap<>(new Person("C", "14-Aug-1994")));

cb.setCellFactory(c -> new ListCell<ComboBoxItemWrap<Person>>() {
        private final CheckBox cb = new CheckBox();

        @Override
        protected void updateItem(ComboBoxItemWrap<Person> item, boolean empty) {
            ComboBoxItemWrap<Person> oldItem = getItem();
            if (oldItem != null) {
                // remove old binding
                cb.selectedProperty().unbindBidirectional(oldItem.checkProperty());
            }

            super.updateItem(item, empty);

            if (empty || item == null) {
                setGraphic(null);
            } else {
                cb.selectedProperty().bindBidirectional(item.checkProperty());
                cb.setText(item.toString());
                setGraphic(cb);
            }
        }
    });

// make sure popup remains open
ComboBoxListViewSkin<ComboBoxItemWrap<Person>> skin = new ComboBoxListViewSkin<>(cb);
skin.setHideOnClick(false);

cb.setSkin(skin);

cb.setItems(options);
cb.promptTextProperty().bind(Bindings.createStringBinding(() ->
    options.stream().filter(ComboBoxItemWrap::getCheck).map(Object::toString).collect(Collectors.joining("; ")), options));

请注意,如果您想在取消选中复选框后关闭弹出窗口,只需为调用cb.arm的复选框添加鼠标释放的事件过滤器,而不是修改皮肤。

谢谢您的回答,但我无法实现它。目前,setHideOnClick方法适用于ComboBoxListViewSkin类型,但在我的例子中,它是ComboBoxListViewSkin。我不确定如何处理这种类型不匹配。@AmitDRoy皮肤和组合框匹配的类型参数。因为我认为您的组合框不可能包含COMPBOX类型的项目,所以COBOBOXListVIEW应该与组合框一起使用。
public class Person {
    private StringProperty name = new SimpleStringProperty();
    private StringProperty birthday = new SimpleStringProperty();

public Person() {
}

public Person(String name, String birthday) {
    setNameValue(name);
    setBirthdayValue(birthday);
}

public StringProperty getNameProperty() {
    return name;
}

public String getNameValue() {
    return name.getValue();
}

public void setNameValue(String value) {
    name.setValue(value);
}

public StringProperty getBirthdayProperty() {
    return birthday;
}

public String getBirthdayValue() {
    return birthday.getValue();
}

public void setBirthdayValue(String value) {
    birthday.setValue(value);
}

@Override
public String toString() {
    return getNameValue()+" ("+getBirthdayValue()+")";
}

}
VBox root = new VBox();
Scene scene = new Scene(root, 450, 250);

ComboBox<ComboBoxItemWrap<Person>> cb = new ComboBox<>();

ObservableList<ComboBoxItemWrap<Person>> options = FXCollections.observableArrayList(item -> new Observable[] {item.checkProperty()});
options.addAll(
        new ComboBoxItemWrap<>(new Person("A", "12-Aug-1994")),
        new ComboBoxItemWrap<>(new Person("B", "13-Aug-1994")),
        new ComboBoxItemWrap<>(new Person("C", "14-Aug-1994")));

cb.setCellFactory(c -> new ListCell<ComboBoxItemWrap<Person>>() {
        private final CheckBox cb = new CheckBox();

        @Override
        protected void updateItem(ComboBoxItemWrap<Person> item, boolean empty) {
            ComboBoxItemWrap<Person> oldItem = getItem();
            if (oldItem != null) {
                // remove old binding
                cb.selectedProperty().unbindBidirectional(oldItem.checkProperty());
            }

            super.updateItem(item, empty);

            if (empty || item == null) {
                setGraphic(null);
            } else {
                cb.selectedProperty().bindBidirectional(item.checkProperty());
                cb.setText(item.toString());
                setGraphic(cb);
            }
        }
    });

// make sure popup remains open
ComboBoxListViewSkin<ComboBoxItemWrap<Person>> skin = new ComboBoxListViewSkin<>(cb);
skin.setHideOnClick(false);

cb.setSkin(skin);

cb.setItems(options);
cb.promptTextProperty().bind(Bindings.createStringBinding(() ->
    options.stream().filter(ComboBoxItemWrap::getCheck).map(Object::toString).collect(Collectors.joining("; ")), options));