【问题标题】:Allow deselection of RadioButton with a ToggleGroup允许使用 ToggleGroup 取消选择 RadioButton
【发布时间】:2017-04-04 22:57:03
【问题描述】:

我试图在 JavaFX 8 中获得一组RadioButtons,其功能最多可以选择一个,但也可以删除任何选择。

似乎RadioButton 只支持取消选择,只要它没有定义ToggleGroup。但是,如果没有定义ToggleGroup,则可以选择多个RadioButton

我尝试为每个按钮的onAction 事件添加一个监听器:

for (final RadioButton rb : myRadioButtons) {
    rb.setToggleGroup(myToggleGroup);
    rb.setOnAction(event -> {
        if (rb.isSelected()) {
            myToggleGroup.selectToggle(null);
        }
    });
}

但是,有两个问题导致此尝试失败 - 第一个是 selected 属性似乎在 action 事件触发之前更新,因此条件始终为真,无法选择 任何按钮。 第二个问题是action 事件即使在单击按钮时被选中,也不会被触发。

mouseClicked 事件添加一个监听器也好不到哪里去,因为它也仅在所选按钮更改后才会触发。

有没有一种直接的方法来实现一组RadioButtons,最多选择一个?或者是唯一支持的选项来处理ToggleGroup 的职责与更改侦听器等?

【问题讨论】:

  • ToggleButton 提供您正在寻找的功能。如果您希望它看起来像一个单选按钮,我想您可以考虑使用 RadioButton 中的 CSS 来自定义一个切换按钮。 (未经测试。可能很棘手。)

标签: javafx radio-button togglebutton


【解决方案1】:
public class RadioButtonTest extends Application {
    @Override
    public void start(Stage primaryStage) {
        Pane root = createPane();
        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }

    private Pane createPane() {
        VBox pane = new VBox();
        ToggleGroup tg = new ToggleGroup();
        RadioButton b1 = new RadioButton("one");
        RadioButton b2 = new RadioButton("two");
        tg.getToggles().addAll(b1, b2);

        RadioButtonSelectionHandler b1Selection = new RadioButtonSelectionHandler(b1);
        b1.setOnMousePressed(b1Selection.getMousePressed());
        b1.setOnMouseReleased(b1Selection.getMouseReleased());

        RadioButtonSelectionHandler b2Selection = new RadioButtonSelectionHandler(b2);
        b2.setOnMousePressed(b2Selection.getMousePressed());
        b2.setOnMouseReleased(b2Selection.getMouseReleased());

        pane.getChildren().addAll(b1, b2);
        pane.setPrefHeight(200);
        pane.setPrefWidth(200);
        return pane;
    }

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

RadioButtonSelectionHandler.java:

public class RadioButtonSelectionHandler {

    private boolean selected = false;

    private RadioButton radioButton;

    public RadioButtonSelectionHandler(RadioButton radioButton) {
        this.radioButton = radioButton;
    }

    EventHandler<MouseEvent> mousePressed = new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent event) {
            if(radioButton.isSelected())
                selected = true;
        }
    };

    EventHandler<MouseEvent> mouseReleased = new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent event) {
            if(selected)
                radioButton.setSelected(false);
            selected = false;
        }
    };

    public EventHandler<MouseEvent> getMousePressed() {
        return mousePressed;
    }

    public EventHandler<MouseEvent> getMouseReleased() {
        return mouseReleased;
    }
}

编辑: 该解决方案有效,但正如@James_D 建议的那样,最好使用 ToggleButton (在发布此答案之前我没有看到他的评论)。不鼓励使用此解决方案,因为您正在寻找的功能在设计上不存在于 RadioButton 中!它在ToggleButton

来自 JavaDoc:

与 RadioButtons 不同,ToggleGroup 中的 ToggleButtons 不会尝试 强制组中至少有一个选定的 ToggleButton。也就是说,如果一个 ToggleButton 被选中,点击它会变成 未选中。使用 RadioButton,单击 组将无效。

【讨论】:

  • 我想这是一个解决方案,但它很笨重。用户习惯于点击一个选定的选项来取消它。
  • 我编辑了我的答案。现在,您无需涉及任何其他控件即可拥有您的功能。
【解决方案2】:

尽管存在用户体验问题或与设计冲突(正如其他人已经提到的),但有两种选择:

  1. 子类 RadioButton 并覆盖其 fire 方法以恢复为 ToggleButton 的行为

  2. 动态添加/删除 mouseHandlers 到选定的组切换

以下是两者的示例(未经正式测试!)。选项一相当简单,选项二有一些怪癖(除了可能依赖于操作系统):

  • eventHandler 必须注册为 eventFilter
  • 必须使用事件
  • 组的selectedToggle 必须为空(相对于取消选择切换)
  • 必须在发布时完成

例子:

public class UnselectableRadioGroup extends Application {
    /**
     * Custom radioBox with behaviour of ToggleButton. 
     */
    public static class MyRadioButton extends RadioButton {

        /**
         * Overridden to revert to ToggleButton behaviour.
         */
        @Override
        public void fire() {
            if (!isDisabled()) {
                setSelected(!isSelected());
                fireEvent(new ActionEvent());
            }
        }

        public MyRadioButton(String text) {
            super(text);
        }

    }

    private EventHandler<MouseEvent> mouseHandler;

    private void addListeners(RadioButton nv) {
        if (mouseHandler == null) {
            mouseHandler = e -> {
                e.consume();
                nv.getToggleGroup().selectToggle(null);
            };
        }
        nv.addEventFilter(MouseEvent.MOUSE_RELEASED, mouseHandler);
    }

    private void removeListeners(RadioButton ov) {
        ov.removeEventFilter(MouseEvent.MOUSE_RELEASED, mouseHandler);
    }

    private Parent getContent() {
        // dynamic un/register event handlers
        ObservableList<RadioButton> radios = FXCollections.observableArrayList(
                new RadioButton("one"), new RadioButton("other"), new RadioButton("third")
                );
        ToggleGroup group = new ToggleGroup();
        group.getToggles().addAll(radios);

        group.selectedToggleProperty().addListener((src, ov, nv) -> {
            if (ov instanceof RadioButton) {
                removeListeners((RadioButton) ov);
            }
            if (nv instanceof RadioButton) {
                addListeners((RadioButton) nv);
            }
        });

        VBox radioBox = new VBox(10);
        radioBox.getChildren().addAll(radios);

        // RadioButton reverted to ToggleButton
        ObservableList<MyRadioButton> toggles = FXCollections.observableArrayList(
                new MyRadioButton("one"), new MyRadioButton("other"), new MyRadioButton("third")
                );
        ToggleGroup myGroup = new ToggleGroup();
        myGroup.getToggles().addAll(toggles);
        VBox myRadioBox = new VBox(10);
        myRadioBox.getChildren().addAll(toggles);

        HBox content = new HBox(10, radioBox, myRadioBox);
        BorderPane pane = new BorderPane(content);
        return pane;
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        Scene scene = new Scene(getContent());
        primaryStage.setScene(scene);
        //primaryStage.setTitle(FXUtils.version());
        primaryStage.show();
    }

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

【讨论】:

    猜你喜欢
    • 2023-03-09
    • 1970-01-01
    • 2016-02-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-10-23
    • 2021-10-15
    • 1970-01-01
    相关资源
    最近更新 更多