Запретить переключение с определенной кнопки-переключателя в группе кнопок, если не выполняется условие
Я хотел бы создать группу из двух кнопок (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 ответа
Интересно... копание немного оказывается, что взаимодействие выбранного / вооруженного / нажатого несколько смущено прерванной проверкой (слабо помню некоторую ошибку, но не могу найти ее прямо сейчас). Основная проблема заключается в том, чтобы не допустить повторного входа в метод 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);
}
После консультации по внедрению 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();
}
}