如何增加ToggleGroup中可能的选择数量(例如RadioButtons)?

时间:2017-09-13 11:56:48

标签: java javafx radio-button togglebutton

我对编程有点陌生,也不熟悉OOP(现在的第二个Java项目),并且喜欢任何提示或帮助。

我目前正在为我自己的笔和纸游戏制作角色创作程序。我使用JavaFX(没有FXML,因此没有SceneBuilder)用于GUI部分。我在JDK1.8.0_131上使用Eclipse Neon。

这是我的问题:
tl; dr:如何增加ToggleGroup中可能的选择数量?
我即将创建一个供用户选择的选项列表。该清单包括他或她可以选择改善其性格的约30种不同优势。允许的最大选择取决于字符,并且在5左右变化。我已经实现了pairs的数组(我知道HashMaps),其中每个条目都是一对由优势的名称组成的表示成本的整数(它们的值不同,因此成本也不同) 现在应该通过

实现列表本身
ScrollPane scrollAdv = new ScrollPane();
VBox vBoxAdv = new VBox();
scrollAdv.setContent(vBoxAdv);
Pair<String, Integer>[] listAdv = info.getAdvantages();
for (int i = 0; i < listAdv.length; i++) {
    String name = listAdv[i].getKey(); // delivers the 1st entry of a pair
    int costs = listAdv[i].getValue(); // delivers the 2nd entry of a pair
    ToggleButton toggleButton = new ToggleButton();
    toggleButton.setUserData(name);
    toggleButton.setText(name + " (" + costs + ")");
    vBoxAdv.getChildren().add(toggleButton);
}

请注意,我不太关心ToggleButtons,他们很容易被RadioButtons取代。如果我正确理解文档,两者都以相同的方式工作。他们都使用ToggleGroup来确保,只选择了一个选项 因此,虽然您现在可能已经猜到了,但我想要做的是让用户一次选择多个选项。我不想这样做,所以用户必须在另一个之后选择一个选项,同时在两者之间重置列表。

感谢您提前阅读并感谢任何帮助或提示。

编辑:我总是可以添加一个计数器,只要选择或取消选择一个选项就会刷新,并阻止任何选择,如果它&lt;&lt; s&lt; 1,但我认为应该有一个更好的解决方案,例如增加ToggleGroup似乎正在使用的内置限制1。

1 个答案:

答案 0 :(得分:1)

一种方法是在达到限制时禁用剩余的切换。这是一个ToggleSet类,它可以做到:

import java.util.ArrayList;
import java.util.List;

import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.IntegerBinding;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;
import javafx.scene.Node ;
import javafx.scene.control.Toggle;

public class ToggleSet<T extends Node & Toggle>  {

    private final ObservableList<T> toggles = FXCollections.observableArrayList(t -> new Observable[] {t.selectedProperty()});
    private final FilteredList<T> selectedToggles = toggles.filtered(t -> ((Toggle)t).isSelected());
    private final IntegerProperty maximumSelectable = new SimpleIntegerProperty(0);

    private final IntegerBinding numSelected = Bindings.size(selectedToggles);

    public ToggleSet(int maximumSelectable) {

        this.maximumSelectable.addListener((obs, oldMax, newMax) -> {
            if (newMax.intValue() < numSelected.get()) {
                List<Toggle> togglesToClear = new ArrayList<>(selectedToggles.subList(0, numSelected.get() - newMax.intValue()));
                togglesToClear.forEach(t -> t.setSelected(false));
            }
        });

        setMaximumSelectable(maximumSelectable);
    }

    public ToggleSet() {
        this(0);
    }

    public ObservableList<T> getSelectedToggles() {
        return FXCollections.unmodifiableObservableList(selectedToggles) ;
    }

    public IntegerProperty maximumSelectableProperty() {
        return maximumSelectable ;
    }

    public final int getMaximumSelectable() {
        return maximumSelectableProperty().get();
    }

    public final void setMaximumSelectable(int maximumSelectable) {
        maximumSelectableProperty().set(maximumSelectable);
    }

    public void addToggle(T toggle) {
        if (numSelected.get() >= getMaximumSelectable()) {
            toggle.setSelected(false);
        }
        toggles.add(toggle);
        toggle.disableProperty().bind(toggle.selectedProperty().not().and(numSelected.greaterThanOrEqualTo(maximumSelectable)));
    }

    public void removeToggle(T toggle) {
        toggles.remove(toggle);
        toggle.disableProperty().unbind();
    }

}

以下是测试它的示例:

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.Spinner;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

public class ToggleSetTest extends Application {

    @Override
    public void start(Stage primaryStage) {
        ToggleSet<ToggleButton> toggleSet = new ToggleSet<>(5);
        GridPane grid = new GridPane() ;

        Spinner<Integer> maxSelectedSpinner = new Spinner<>(0, 20, 5);
        maxSelectedSpinner.getValueFactory().valueProperty().bindBidirectional(toggleSet.maximumSelectableProperty().asObject());

        grid.add(new HBox(2, new Label("Maximum selected"), maxSelectedSpinner), 0, 0, 2, 1);

        grid.addRow(1,  new Label("Selection"), new Label("Include in set"));

        for (int i = 1; i <= 20 ; i++) {
            RadioButton button = new RadioButton("Button "+i);
            CheckBox checkBox = new CheckBox();
            checkBox.selectedProperty().addListener((obs, wasChecked, isNowChecked) -> {
                if (isNowChecked) {
                    toggleSet.addToggle(button);
                } else {
                    toggleSet.removeToggle(button);
                }
            });
            checkBox.setSelected(true);
            grid.addRow(i + 1, button, checkBox);
        }
        grid.setPadding(new Insets(10));
        grid.setHgap(5);
        grid.setVgap(2);
        Scene scene = new Scene(grid);
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

如果您希望与ToggleGroup具有相同的行为,而前一个选项未被选中,则会有点棘手,但以下情况应该有效:

import java.util.ArrayList;
import java.util.List;

import javafx.beans.Observable;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.value.ChangeListener;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Node ;
import javafx.scene.control.Toggle;

public class ToggleSet<T extends Node & Toggle>  {

    private final ObservableList<T> toggles = FXCollections.observableArrayList(t -> new Observable[] {t.selectedProperty()});
    private final ObservableList<T> selectedToggles = FXCollections.observableArrayList();
    private final IntegerProperty maximumSelectable = new SimpleIntegerProperty(0);

    private final ChangeListener<Boolean> toggleListener = (obs, wasSelected, isNowSelected) -> {
        @SuppressWarnings("unchecked")
        T toggle = (T) ((Property<?>)obs).getBean();
        if (isNowSelected) {
            selectedToggles.add(toggle);
            ensureWithinMax();
        } else {
            selectedToggles.remove(toggle);
        }
    };

    public ToggleSet(int maximumSelectable) {

        this.maximumSelectable.addListener((obs, oldMax, newMax) -> ensureWithinMax());
        setMaximumSelectable(maximumSelectable);
    }

    private void ensureWithinMax() {
        if (this.maximumSelectable.get() < selectedToggles.size()) {
            List<Toggle> togglesToClear = new ArrayList<>(selectedToggles.subList(0, selectedToggles.size() - this.maximumSelectable.get()));
            togglesToClear.forEach(t -> t.setSelected(false));
        }
    }

    public ToggleSet() {
        this(0);
    }

    public ObservableList<T> getSelectedToggles() {
        return FXCollections.unmodifiableObservableList(selectedToggles) ;
    }

    public IntegerProperty maximumSelectableProperty() {
        return maximumSelectable ;
    }

    public final int getMaximumSelectable() {
        return maximumSelectableProperty().get();
    }

    public final void setMaximumSelectable(int maximumSelectable) {
        maximumSelectableProperty().set(maximumSelectable);
    }

    public void addToggle(T toggle) {
        if (toggle.isSelected()) {
            selectedToggles.add(toggle);
            ensureWithinMax();
        }
        toggle.selectedProperty().addListener(toggleListener);
        toggles.add(toggle);
    }

    public void removeToggle(T toggle) {
        toggle.selectedProperty().removeListener(toggleListener);
        toggles.remove(toggle);
    }

}

(使用相同的测试代码。)