如何通过 JavaFX 中的单选按钮限制最大可选复选框?
How to put constrains on maximum selectable CheckBoxes via RadioButtons in JavaFX?
请看下图,你就会明白我的应用程序的布局。
我希望能够动态选择多少 CheckBox
(启用下拉菜单)是可选的(固定数量)。我想用这 3 个 RadioButton
.
来实现
在垂直模式下,必须选择所有 4 个 CheckBox
(不少于)。在混合模式下,只有 2 CheckBox
必须可用(不多也不少)。在水平模式下,仅需选择 1 CheckBox
(不能更多)。重要的是用户能够选择特定的组合 ComboBox
es(例如:我们处于混合模式,选择 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
.
来操纵它们
您可以在所有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();
}
}
有几点需要注意。您将需要命名容器 enable/disable 适当的选择区域。这里它们被称为 hbSelection#
,这是我们添加复选框和组合框的地方。
在监听器中,您会看到我基本上只是根据 newValue
设置每个 HBox 的禁用 属性,如果选择 RadioButton
则为 true
false
如果不是。
可能有更有效的方法来处理这个问题,但这绝对是一种方法。
我不建议对 CheckBox
es/ComboBox
es 和不同地方的案例进行硬编码。使用相同的值来创建 CheckBox
es/ComboBox
es 和修改允许的 selected CheckBox
es 数量。但是,这会阻止您单独在 fxml 中创建外观。
除非你想让用户出现一些相当混乱的行为,否则你需要允许用户 select 少于所需的 CheckBox
数量,因为你无法真正分辨出哪个 CheckBox
到 select 当取消 select 一个。您可以 disable/enable 提交表单的按钮或一些类似的控件...
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;
}
}
请看下图,你就会明白我的应用程序的布局。
我希望能够动态选择多少 CheckBox
(启用下拉菜单)是可选的(固定数量)。我想用这 3 个 RadioButton
.
在垂直模式下,必须选择所有 4 个 CheckBox
(不少于)。在混合模式下,只有 2 CheckBox
必须可用(不多也不少)。在水平模式下,仅需选择 1 CheckBox
(不能更多)。重要的是用户能够选择特定的组合 ComboBox
es(例如:我们处于混合模式,选择 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
.
您可以在所有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();
}
}
有几点需要注意。您将需要命名容器 enable/disable 适当的选择区域。这里它们被称为 hbSelection#
,这是我们添加复选框和组合框的地方。
在监听器中,您会看到我基本上只是根据 newValue
设置每个 HBox 的禁用 属性,如果选择 RadioButton
则为 true
false
如果不是。
可能有更有效的方法来处理这个问题,但这绝对是一种方法。
我不建议对 CheckBox
es/ComboBox
es 和不同地方的案例进行硬编码。使用相同的值来创建 CheckBox
es/ComboBox
es 和修改允许的 selected CheckBox
es 数量。但是,这会阻止您单独在 fxml 中创建外观。
除非你想让用户出现一些相当混乱的行为,否则你需要允许用户 select 少于所需的 CheckBox
数量,因为你无法真正分辨出哪个 CheckBox
到 select 当取消 select 一个。您可以 disable/enable 提交表单的按钮或一些类似的控件...
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;
}
}