除非满足条件,否则禁止从按钮组中的特定切换按钮切换

时间:2013-10-16 09:16:47

标签: java swing buttongroup jtogglebutton

我想创建一组两个按钮(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);
            }
        }

    }
}

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

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

修改

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

2 个答案:

答案 0 :(得分: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);
}

答案 1 :(得分:1)

在咨询ButtonGroup的实现后,我意识到调用ButtonGroup.setSelected(ButtonModel, boolean)可能会产生相同方法的新调用,导致我的状况被检查三次。所以我现在要防范后续的电话。似乎工作。但是有点不确定。

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();
    }

}