有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

java如何通过JavaFX中的单选按钮对最大可选复选框进行约束?

请看下面的图片,你会理解我的应用程序的布局

enter image description here

我希望能够动态选择可选择的CheckBox(启用下拉菜单)数量(固定数量)。我想通过这3 RadioButton来实现这一点

在垂直模式下,必须选择所有4CheckBox(不少于)。在混合模式下,只有2CheckBox可用(不能多也不能少)。在水平模式下,只能选择1CheckBox(不能选择更多)。重要的是,用户能够选择特定的组合ComboBox(例如:我们处于混合模式,选择1和2与选择1和3不同)

解决方案

public class ConfigurationEditDialogController {

// Data Acquisition Tab

private ObservableList<String> options =
        FXCollections.observableArrayList(
                "ciao",
                "hello",
                "halo"
        );

@FXML
private PrefixSelectionComboBox<String> testBus1ComboBox = new PrefixSelectionComboBox<>();
@FXML
private PrefixSelectionComboBox<String> testBus2ComboBox = new PrefixSelectionComboBox<>();
@FXML
private PrefixSelectionComboBox<String> testBus3ComboBox = new PrefixSelectionComboBox<>();
@FXML
private PrefixSelectionComboBox<String> testBus4ComboBox = new PrefixSelectionComboBox<>();
@FXML
private CheckBox checkbox1;
@FXML
private CheckBox checkbox2;
@FXML
private CheckBox checkbox3;
@FXML
private CheckBox checkbox4;

private ObservableSet<CheckBox> selectedCheckBoxes = FXCollections.observableSet();
private ObservableSet<CheckBox> unselectedCheckBoxes = FXCollections.observableSet();

private IntegerBinding numCheckBoxesSelected = Bindings.size(selectedCheckBoxes);
private int maxNumSelected =  2;

@FXML
private RadioButton verticalMode;
@FXML
private RadioButton horizontalMode;
@FXML
private RadioButton hybridMode;


private Stage dialogStage;
private Configuration configuration;
private boolean okClicked = false;

/**
 * Initializes the controller class. This method is automatically called
 * after the fxml file has been loaded.
 */
@FXML
private void initialize() {
    testBus1ComboBox.setItems(options);
    testBus2ComboBox.setItems(options);
    testBus3ComboBox.setItems(options);
    testBus4ComboBox.setItems(options);
    configureCheckBox(checkbox1);
    configureCheckBox(checkbox2);
    configureCheckBox(checkbox3);
    configureCheckBox(checkbox4);

    numCheckBoxesSelected.addListener((obs, oldSelectedCount, newSelectedCount) -> {
        if (newSelectedCount.intValue() >= maxNumSelected) {
            unselectedCheckBoxes.forEach(cb -> cb.setDisable(true));
        } else {
            unselectedCheckBoxes.forEach(cb -> cb.setDisable(false));
        }
    });


    testBus1ComboBox.disableProperty().bind(checkbox1.selectedProperty().not());
    testBus2ComboBox.disableProperty().bind(checkbox2.selectedProperty().not());
    testBus3ComboBox.disableProperty().bind(checkbox3.selectedProperty().not());
    testBus4ComboBox.disableProperty().bind(checkbox4.selectedProperty().not());

}


private void configureCheckBox(CheckBox checkBox) {

    if (checkBox.isSelected()) {
        selectedCheckBoxes.add(checkBox);
    } else {
        unselectedCheckBoxes.add(checkBox);
    }

    checkBox.selectedProperty().addListener((obs, wasSelected, isNowSelected) -> {
        if (isNowSelected) {
            unselectedCheckBoxes.remove(checkBox);
            selectedCheckBoxes.add(checkBox);
        } else {
            selectedCheckBoxes.remove(checkBox);
            unselectedCheckBoxes.add(checkBox);
        }

    });

}

FXML文件的选项卡 我想在这个选项卡中实现fabian解决方案,但是不需要像我那样使用fxml

<Tab closable="false" text="Data Acquisition">
           <content>
                <GridPane prefHeight="254.0" prefWidth="404.0">
                    <columnConstraints>
                        <ColumnConstraints hgrow="SOMETIMES" maxWidth="218.0" minWidth="10.0" prefWidth="111.0" />
                        <ColumnConstraints hgrow="SOMETIMES" maxWidth="519.0" minWidth="10.0" prefWidth="490.0" />
                    <ColumnConstraints hgrow="SOMETIMES" maxWidth="316.0" minWidth="10.0" prefWidth="71.0" />
                    </columnConstraints>
                    <rowConstraints>
                    <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                        <RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
                    </rowConstraints>
                    <children>
                        <Label text="Test Bus 1" GridPane.rowIndex="2" />
                        <Label text="Test Bus 2" GridPane.rowIndex="3" />
                        <Label text="Test Bus 3" GridPane.rowIndex="4" />
                        <Label text="Test Bus 4" GridPane.rowIndex="5" />
                    <PrefixSelectionComboBox fx:id="testBus1ComboBox" prefHeight="31.0" prefWidth="300.0" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="2" />
                    <PrefixSelectionComboBox fx:id="testBus2ComboBox" prefWidth="300.0" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="3" />
                    <PrefixSelectionComboBox fx:id="testBus3ComboBox" prefWidth="300.0" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="4" />
                    <PrefixSelectionComboBox fx:id="testBus4ComboBox" prefWidth="300.0" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="5" />
                    <CheckBox fx:id="checkbox1" mnemonicParsing="false" GridPane.columnIndex="2" GridPane.rowIndex="2" />
                    <CheckBox fx:id="checkbox2" mnemonicParsing="false" GridPane.columnIndex="2" GridPane.rowIndex="3" />
                    <CheckBox fx:id="checkbox3" mnemonicParsing="false" GridPane.columnIndex="2" GridPane.rowIndex="4" />
                    <CheckBox fx:id="checkbox4" mnemonicParsing="false" GridPane.columnIndex="2" GridPane.rowIndex="5" />
                    <Label text="Sample Mode" GridPane.rowIndex="1" />
                    <RadioButton fx:id="verticalMode" mnemonicParsing="false" selected="true" text="Vertical " GridPane.columnIndex="1" GridPane.rowIndex="1">
                       <toggleGroup>
                          <ToggleGroup fx:id="SampleModeGroup" />
                       </toggleGroup>
                    </RadioButton>
                    <RadioButton fx:id="hybridMode" mnemonicParsing="false" text="Hybrid" toggleGroup="$SampleModeGroup" GridPane.columnIndex="1" GridPane.halignment="CENTER" GridPane.rowIndex="1" />
                    <RadioButton fx:id="horizontalMode" mnemonicParsing="false" text="Horizontal" toggleGroup="$SampleModeGroup" GridPane.columnIndex="1" GridPane.halignment="RIGHT" GridPane.rowIndex="1" />
                    </children>
                </GridPane>
           </content>
        </Tab>

我只使用private int maxNumSelected = 2;手动设置了允许的最大值(而不是最小值)。然而,我想通过RadioButton操纵它们


共 (2) 个答案

  1. # 1 楼答案

    您可以在所有RadioButton上设置一个侦听器,当选择一个侦听器时,禁用或启用每个ComboBox/CheckBox节点的容器

    下面是一个示例应用程序,演示了这一点。我用纯Java(没有FXML)构建UI,只是为了将所有内容保存在一篇文章中。重要的部分是RadioButtons中添加的三个侦听器

    import javafx.application.Application;
    import javafx.geometry.Insets;
    import javafx.geometry.Pos;
    import javafx.scene.Scene;
    import javafx.scene.control.CheckBox;
    import javafx.scene.control.ComboBox;
    import javafx.scene.control.RadioButton;
    import javafx.scene.control.ToggleGroup;
    import javafx.scene.layout.HBox;
    import javafx.scene.layout.VBox;
    import javafx.stage.Stage;
    
    public class Main extends Application {
    
        public static void main(String[] args) {
            launch(args);
        }
    
        @Override
        public void start(Stage stage) throws Exception {
    
            // Root layout
            VBox root = new VBox(5);
            root.setPadding(new Insets(10));
            root.setAlignment(Pos.CENTER);
    
            // Radio buttons
            HBox hbRadios = new HBox(10);
            hbRadios.setAlignment(Pos.CENTER);
            ToggleGroup tglRadioSelections = new ToggleGroup();
            RadioButton rdoVertical = new RadioButton("Vertical");
            RadioButton rdoHybrid = new RadioButton("Hybrid");
            RadioButton rdoHorizontal = new RadioButton("Horizontal");
            tglRadioSelections.getToggles().addAll(rdoVertical, rdoHybrid, rdoHorizontal);
            hbRadios.getChildren().addAll(rdoVertical, rdoHybrid, rdoHorizontal);
    
            // ComboBoxes and CheckBoxes
            VBox vbSelections = new VBox(10);
            ComboBox cbo1 = new ComboBox();
            ComboBox cbo2 = new ComboBox();
            ComboBox cbo3 = new ComboBox();
            ComboBox cbo4 = new ComboBox();
            CheckBox chk1 = new CheckBox();
            CheckBox chk2 = new CheckBox();
            CheckBox chk3 = new CheckBox();
            CheckBox chk4 = new CheckBox();
    
            // Create the containers for each selection group
            HBox hbSelection1 = new HBox(10);
            hbSelection1.getChildren().addAll(cbo1, chk1);
            HBox hbSelection2 = new HBox(10);
            hbSelection2.getChildren().addAll(cbo2, chk2);
            HBox hbSelection3 = new HBox(10);
            hbSelection3.getChildren().addAll(cbo3, chk3);
            HBox hbSelection4 = new HBox(10);
            hbSelection4.getChildren().addAll(cbo4, chk4);
    
            // Add listeners for each radio button to enable appropriate selections
            rdoVertical.selectedProperty().addListener((observableValue, oldValue, newValue) -> {
                    hbSelection1.setDisable(!newValue);
                    hbSelection2.setDisable(!newValue);
                    hbSelection3.setDisable(!newValue);
                    hbSelection4.setDisable(!newValue);
            });
            rdoHybrid.selectedProperty().addListener((observableValue, oldValue, newValue) -> {
                hbSelection1.setDisable(!newValue);
                hbSelection2.setDisable(!newValue);
                hbSelection3.setDisable(!newValue);
                hbSelection4.setDisable(newValue);
            });
            rdoHorizontal.selectedProperty().addListener((observableValue, oldValue, newValue) -> {
                hbSelection1.setDisable(!newValue);
                hbSelection2.setDisable(newValue);
                hbSelection3.setDisable(newValue);
                hbSelection4.setDisable(newValue);
            });
    
            // Build the scene
            vbSelections.getChildren().addAll(hbSelection1, hbSelection2, hbSelection3, hbSelection4);
            root.getChildren().addAll(hbRadios, vbSelections);
            stage.setScene(new Scene(root));
    
            stage.show();
        }
    }
    

    有几件事需要注意。您将需要命名容器来启用/禁用适当的选择区域。在这里,它们被称为hbSelection#,这是我们添加复选框和组合框的地方

    在监听器中,您将看到我基本上只是基于newValue设置每个HBox的disabled属性,如果选择了RadioButton,则设置为true,如果未选择则设置为false

    也许有一种更有效的方法来处理这个问题,但这绝对是一种方法

  2. # 2 楼答案

    我不建议硬编码CheckBoxes/ComboBoxes和不同地方的病例计数。使用相同的值创建CheckBoxes/ComboBoxes和修改所选CheckBoxes的允许数量。但是,这会妨碍您仅在fxml中创建外观

    除非您希望用户有一些相当混乱的行为,否则您需要允许用户选择少于所需数量的CheckBox,因为在取消选择时,您无法真正判断要选择哪个CheckBox。您可以禁用/启用用于提交表单的按钮或一些类似控件

    private static HBox createModesRadios(IntegerProperty count, Mode... modes) {
        ToggleGroup group = new ToggleGroup();
        HBox result = new HBox(10);
        for (Mode mode : modes) {
            RadioButton radio = new RadioButton(mode.getText());
            radio.setToggleGroup(group);
            radio.setUserData(mode);
            result.getChildren().add(radio);
        }
        if (modes.length > 0) {
            group.selectToggle((Toggle) result.getChildren().get(0));
            count.bind(Bindings.createIntegerBinding(() -> ((Mode) group.getSelectedToggle().getUserData()).getCount(), group.selectedToggleProperty()));
        } else {
            count.set(0);
        }
        return result;
    }
    
    private static void updateCheckBoxes(CheckBox[] checkBoxes, int requiredCount, int unmodifiedIndex) {
        if (unmodifiedIndex >= 0 && checkBoxes[unmodifiedIndex].isSelected()) {
            requiredCount ;
        }
        int i;
        for (i = 0; i < checkBoxes.length && requiredCount > 0; i++) {
            if (i != unmodifiedIndex && checkBoxes[i].isSelected()) {
                requiredCount ;
            }
        }
    
        for (; i < checkBoxes.length; i++) {
            if (i != unmodifiedIndex) {
                checkBoxes[i].setSelected(false);
            }
        }
    }
    
    @Override
    public void start(Stage primaryStage) {
        Mode[] modes = new Mode[]{
            new Mode("Vertical", 4),
            new Mode("Hybrid", 2),
            new Mode("Horizontal", 1)
        };
    
        ToggleGroup group = new ToggleGroup();
        IntegerProperty elementCount = new SimpleIntegerProperty();
    
        HBox radioBox = createModesRadios(elementCount, modes);
        GridPane grid = new GridPane();
        VBox root = new VBox(10, radioBox);
    
        int count = Stream.of(modes).mapToInt(Mode::getCount).max().orElse(0);
        ObservableMap<Integer, String> elements = FXCollections.observableHashMap();
    
        ObservableList<String> options = FXCollections.observableArrayList(
                "ciao",
                "hello",
                "halo");
    
        CheckBox[] checkBoxes = new CheckBox[count];
    
        elementCount.addListener((o, oldValue, newValue) -> {
            // uncheck checkboxes, if too many are checked
            updateCheckBoxes(checkBoxes, newValue.intValue(), -1);
        });
    
        for (int i = 0; i < count; i++) {
            final Integer index = i;
            CheckBox checkBox = new CheckBox();
            checkBoxes[i] = checkBox;
    
            ComboBox<String> comboBox = new ComboBox<>(options);
            comboBox.valueProperty().addListener((o, oldValue, newValue) -> {
                // modify value in map on value change
                elements.put(index, newValue);
            });
            comboBox.setDisable(true);
    
            checkBox.selectedProperty().addListener((o, oldValue, newValue) -> {
                comboBox.setDisable(!newValue);
                if (newValue) {
                    // put the current element in the map
                    elements.put(index, comboBox.getValue());
    
                    // uncheck checkboxes that exceede the required count keeping the current one unmodified
                    updateCheckBoxes(checkBoxes, elementCount.get(), index);
                } else {
                    elements.remove(index);
                }
    
            });
            grid.addRow(i, comboBox, checkBox);
        }
    
        Button submit = new Button("submit");
        submit.setOnAction(evt -> System.out.println(elements));
    
        // enable submit button iff the number of elements is correct
        submit.disableProperty().bind(elementCount.isNotEqualTo(Bindings.size(elements)));
    
        root.getChildren().addAll(grid, submit);
    
        final Scene scene = new Scene(root, 400, 400);
    
        primaryStage.setScene(scene);
        primaryStage.show();
    }
    
    public class Mode {
    
        private final String text;
        private final int count;
    
        public Mode(String text, int count) {
            this.text = text;
            this.count = count;
        }
    
        public String getText() {
            return text;
        }
    
        public int getCount() {
            return count;
        }
    
    }