请看下面的图片,您将了解我的应用程序的布局。
我希望可以动态选择CheckBox
(启用下拉菜单)的数量(固定数量)。我想用那三个RadioButton
来实现。
在垂直模式下,必须全部选择4 CheckBox
(不少于)。在混合模式下,仅2 CheckBox
必须可用(不多也不少)。在水平模式下,仅必须选择1 CheckBox
(不能更多)。用户必须具有选择特定组合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
来操纵它们。
答案 0 :(得分:2)
您可以在所有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的禁用属性,如果选择了true
,则为RadioButton
;如果选择了false
,则为不是。
也许有一种更有效的处理方法,但这绝对是一种方法。
答案 1 :(得分:1)
我不建议在不同位置对CheckBox
es / ComboBox
es和计数进行硬编码。创建CheckBox
es / ComboBox
es和修改允许的选定CheckBox
es数量时,请使用相同的值。但是,这可以防止您仅在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;
}
}