Мне было любопытно, поэтому я решил попробовать эту проблему.Как вы узнали, это сложнее, чем кажется, потому что любой код, который вы пишете в WindowAdapter
, всегда будет срабатывать до того, как родительское окно и кнопка получит фокус, и поэтому диалог будет закрыт.
Я считаю, что решение состоит в том, чтобы убедиться, что кнопка отключена, пока диалоговое окно не было закрыто на некоторое время, и это то, что я сделал.Я отключаю кнопку, когда диалог закрывается.Вторая проблема заключалась в том, чтобы найти способ снова включить кнопку, но только после обработки события нажатия кнопки мыши, в противном случае кнопка будет нажата, и диалоговое окно снова появится снова.
Мое первое использованное решение использовалосьjavax.swing.Timer
, который был настроен на однократное срабатывание при потере фокуса диалога с задержкой в 100 мс, после чего кнопка снова будет активирована.Это сработало, потому что небольшая задержка гарантировала, что кнопка не была включена до тех пор, пока событие нажатия уже не перешло к кнопке, и, поскольку кнопка все еще была заблокирована, она не была нажата.
Второе решение,который я публикую здесь, лучше, потому что не требуется никаких таймеров или задержек.Я просто обертываю вызов, чтобы снова включить кнопку в SwingUtilities.invokeLater
, которая помещает это событие в КОНЕЦ очереди событий.На этом этапе событие нажатия мыши уже находится в очереди, поэтому действие по включению кнопки гарантированно произойдет после этого, поскольку Swing обрабатывает события строго по порядку.Отключение и включение кнопки происходит так внезапно, что вы вряд ли увидите, как это происходит, но этого достаточно, чтобы помешать вам нажимать кнопку, пока диалоговое окно не исчезнет.
В примере кода есть метод main, который помещаеткнопка в JFrame
.Вы можете открыть диалоговое окно, а затем заставить его потерять фокус, нажав кнопку или нажав строку заголовка окна.Я реорганизовал ваш исходный код, чтобы кнопка отвечала только за отображение и скрытие указанного диалога, чтобы вы могли повторно использовать его для отображения любого диалогового окна по вашему желанию.
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
public class QuickDialogButton extends JButton {
private final JDialog dialog;
public QuickDialogButton(String label, JDialog d) {
super(label);
dialog = d;
dialog.addWindowListener(new WindowAdapter() {
public void windowDeactivated(WindowEvent e) {
// Button will be disabled when we return.
setEnabled(false);
dialog.setVisible(false);
// Button will be enabled again when all other events on the queue have finished.
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
setEnabled(true);
}
});
}
});
addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Component c = (Component) e.getSource();
dialog.setLocation(c.getLocationOnScreen().x, c.getLocationOnScreen().y + c.getHeight());
dialog.setVisible(true);
}
});
}
public static void main(String[] args) {
JFrame f = new JFrame("Parent Window");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JDialog d = new JDialog(f, "Child Dialog");
d.setDefaultCloseOperation(WindowConstants.HIDE_ON_CLOSE);
d.add(new JCheckBox("Something"));
d.setUndecorated(true);
d.pack();
f.add(new QuickDialogButton("Button", d));
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}