WindowListener не работает должным образом
Я хочу, чтобы мой GUI делал некоторые проверки, когда появляется JOptionPane. Поскольку я не могу найти какой-либо другой способ, я, тем не менее, могу делать это каждый раз, когда окно приложения теряет фокус (это просто проверка строки). По этой причине я добавил следующий код в мой JFrame:
appFrame.addWindowListener(new WindowAdapter() {
@Override
public void windowLostFocus(WindowEvent e) {
System.out.println("Focus Lost");
}
@Override
public void windowClosing(WindowEvent e) {
//some other stuff here that work
}
});
Слушатель закрытия окна работает нормально. Хотя, когда JFrame не сфокусирован, ничего не происходит. Не следует ли печатать "Focus Lost" каждый раз, когда я переключаюсь с JFrame на какое-то другое окно? Кроме того, этот метод будет запущен, когда показывается JOptionPane?
3 ответа
Я не буду вдаваться в подробности, почему вы делаете то, что делаете, но это не работает, как вы ожидаете, по следующей причине:
WindowAdapter
это удобный класс, поэтому вы можете создать одного слушателя и зарегистрировать его для нескольких типов событий. Вы зарегистрировали его только для одного набора событий, вам нужно также зарегистрировать его для событий фокуса через: Window.addWindowFocusListener()
WindowAdapter adapter = new WindowAdapter() {
@Override
public void windowLostFocus(WindowEvent e) {
System.out.println("Focus Lost");
}
@Override
public void windowClosing(WindowEvent e) {
//some other stuff here that work
}
};
appFrame.addWindowListener(adapter);
appFrame.addWindowFocusListener(adapter);
Ключевым для меня является то, что вы хотите, чтобы изменение в графическом интерфейсе вызвано изменением строковой переменной. Лучший способ решить эту проблему - сделать переменную String связанным свойством с помощью PropertyChangeListenerSupport. Таким образом, вы можете заставить GUI присоединять PropertyChangeListener к классу, который содержит переменную String, а затем получать уведомления об изменениях, позволяя вам соответствующим образом обновить GUI.
Если вы идете по этому пути, рассмотрите возможность предоставления наблюдаемому классу поля SwingPropertyChangeSupport, чтобы слушатели были уведомлены о потоке событий Swing и, надеюсь, избежали любых проблем параллелизма Swing.
Вот краткий пример:
import java.awt.Dimension;
import java.awt.event.*;
import java.beans.*;
import javax.swing.*;
import javax.swing.event.SwingPropertyChangeSupport;
public class ShowPropertyChangeSupport {
@SuppressWarnings("serial")
private static void createAndShowGui() {
final MainGUI mainGui = new MainGUI("Title");
final ObservedClass observedClass = new ObservedClass();
observedClass.addPropertyChangeListener(new PropertyChangeListener() {
@Override
public void propertyChange(PropertyChangeEvent pcEvt) {
if (pcEvt.getPropertyName().equals(ObservedClass.BOUND_PROPERTY)) {
mainGui.setTitle(pcEvt.getNewValue().toString());
}
}
});
mainGui.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainGui.pack();
mainGui.setLocationRelativeTo(null);
mainGui.setVisible(true);
int timerDelay = 6000; // every 6 seconds
new Timer(timerDelay, new ActionListener() {
@Override
public void actionPerformed(ActionEvent arg0) {
String result = JOptionPane.showInputDialog(mainGui,
"Please enter a String", "Set GUI title", JOptionPane.PLAIN_MESSAGE);
if (result != null) {
observedClass.setBoundProperty(result);
}
}
}){{setInitialDelay(1000);}}.start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
// ** note that I don't like extending JFrame,
// but will do this for sake of example simplicity
class MainGUI extends JFrame {
public MainGUI(String title) {
super(title);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(400, 300);
}
}
class ObservedClass {
public static final String BOUND_PROPERTY = "bound property";
private String boundProperty = "";
private SwingPropertyChangeSupport spcSupport = new SwingPropertyChangeSupport(
this);
public SwingPropertyChangeSupport getSpcSupport() {
return spcSupport;
}
public void setSpcSupport(SwingPropertyChangeSupport spcSupport) {
this.spcSupport = spcSupport;
}
public String getBoundProperty() {
return boundProperty;
}
public void setBoundProperty(String boundProperty) {
String oldValue = this.boundProperty;
String newValue = boundProperty;
this.boundProperty = newValue;
spcSupport.firePropertyChange(BOUND_PROPERTY, oldValue, newValue);
}
public void addPropertyChangeListener(PropertyChangeListener listener) {
spcSupport.addPropertyChangeListener(listener);
}
public void removePropertyChangeListener(PropertyChangeListener listener) {
spcSupport.removePropertyChangeListener(listener);
}
}
Ключ ко всему этому, на мой взгляд, заключается в том, чтобы использовать прослушиватель так, чтобы класс со связанным свойством - прослушиваемой строкой - не знал ни GUI, ни слушателя, ни GUI, а также не знал о класс со связанным свойством. Они полностью отделены.
1) JOptionPane
/ модальный JDialog
есть проблема модальности, но модальность может быть преимуществом, если у всех контейнеров есть собственный владелец, для реального обхода проблемы вы должны знать (я буду говорить о том, как я могу это проверить)
числа
Window[]
, и еслиisDisplayable()
, то вы можете использовать следующиевы можете получить SwingUtilities#getAccessibleIndexInXxx может вернуть AccessibleState
KeyboardFocusManager (очень интересные методы для мультитач) возвращает методы getXxxFocusXxx
Focus, FocusSubsystem довольно асинхронный,
2) Пожалуйста, с должным уважением, я не знаю, зачем вам это нужно, по каким причинам мне нужно знать об этом, речь идет о бизнес-правилах, вы всегда должны знать.... и если это делается на EDT
- Focus, FocusSubsystem довольно асинхронный,