2

我想创建一组两个按钮(A 和 B),其中始终允许从 B 切换到 A,但从 A 切换到 B 取决于条件。只能在后一种情况下(A 到 B)检查条件,并且每次切换尝试只能进行一次检查。如果阻止切换,则必须没有ItemEvent.SELECTED为任一按钮生成事件。

看起来很简单,所以我很困惑为什么我不能以一种简单而简洁的方式做到这一点。我认为扩展 ButtonGroup 是这样做的方法,但现在我不确定了。

import java.awt.FlowLayout;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.ButtonGroup;
import javax.swing.ButtonModel;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JToggleButton;
import javax.swing.SwingUtilities;

public class ToggleGroup extends JFrame {

    private ButtonGroup group = new MyButtonGroup();
    private JToggleButton buttonA = new JToggleButton("A");
    private JToggleButton buttonB = new JToggleButton("B");

    public ToggleGroup() {
        setLayout(new FlowLayout());
        add(buttonA);
        add(buttonB);
        group.add(buttonA);
        group.add(buttonB);
        group.setSelected(buttonA.getModel(), true);
        pack();
        setLocationRelativeTo(null);

        ItemListener itemListener = new ItemListener() {
            public void itemStateChanged(ItemEvent e) {                
                if (e.getStateChange() == ItemEvent.SELECTED) {
                    System.out.println("-> " + (e.getSource() == buttonA ? "A" : "B") + " selected");
                }
            }
        };
        buttonA.addItemListener(itemListener);
        buttonB.addItemListener(itemListener);
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                ToggleGroup test = new ToggleGroup();
                test.setVisible(true);
            }
        });
    }

    private class MyButtonGroup extends ButtonGroup {

        private boolean check() {
            int result = JOptionPane.showConfirmDialog(
                    ToggleGroup.this, "May I switch to B?",
                    "Important question", JOptionPane.YES_NO_OPTION,
                    JOptionPane.WARNING_MESSAGE);
            return result == JOptionPane.YES_OPTION;
        }

        @Override
        public void setSelected(ButtonModel m, boolean b) {      
            if (!b) {
                return;
            }
            if (m == buttonA.getModel() || m == buttonB.getModel() && check()) {
                super.setSelected(m, b);
            }
        }

    }
}

我的代码的问题很明显。条件被多次检查,因此该对话框也被多次显示。

那么,当条件失败时,我如何“消耗”切换尝试呢?

编辑:

我使用这些按钮的上下文是在应用程序的不同模式之间切换的实现。其中一种模式允许更改数据并稍后提交。如果存在未提交的更改,则切换模式可能意味着数据丢失。我想确保开关是故意的。在满足条件之前禁用任何一个按钮不是一种选择。

4

2 回答 2

1

在咨询了实现之后,ButtonGroup我意识到调用ButtonGroup.setSelected(ButtonModel, boolean)可能会产生相同方法的新调用,导致我的状况被检查多达 3 次。所以我现在提防后续调用。似乎工作。不过,有点不确定。

private class MyButtonGroup extends ButtonGroup {

    private Stack<ButtonModel> locked = new Stack<ButtonModel>();

    @Override
    public void setSelected(ButtonModel m, boolean b) {      
        if (!b || !locked.isEmpty() && locked.peek() == m) {
            return;
        }
        locked.push(m);
        if (m == buttonA.getModel() || m == buttonB.getModel() && check()) {
            super.setSelected(m, b);
        }
        locked.pop();
    }

}
于 2013-10-16T12:09:06.457 回答
1

有趣的......挖掘了一下发现,被选中/武装/按下的交互有点被中断的检查所混淆(隐约记得一些错误,但现在找不到它)。主要问题是不允许重新进入 setSelected 方法。一个肮脏的(阅读:没有进一步挖掘)的出路是切换一个标志,比如

private boolean isChecking;
@Override
public void setSelected(final ButtonModel m, boolean b) {   
    if (isChecking) return;
    isChecking = false;
    if (!b) {
        return;
    }
    if (m == buttonB.getModel()) {
        isChecking = true;
        final boolean select = check();
        if (select) {
            superSetSelected(m, select);
        }
        isChecking = false;
        return;
    } else {
        superSetSelected(m, b);
    }
}

protected void superSetSelected(ButtonModel m, boolean b) {
    super.setSelected(m, b);
}
于 2013-10-16T12:08:38.337 回答