Как правило, вызова метода setVisible
достаточно для отображения или скрытия компонента Swing.
Просто чтобы убедиться, что это работает, я попробовал следующее:
public class Visibility {
private void makeGUI() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JLabel l = new JLabel("Hello");
final JButton b = new JButton("Hide Label");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
l.setVisible(false);
}
});
f.getContentPane().setLayout(new BorderLayout());
f.getContentPane().add(l, BorderLayout.CENTER);
f.getContentPane().add(b, BorderLayout.SOUTH);
f.setSize(200, 200);
f.setLocation(200, 200);
f.validate();
f.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new Visibility().makeGUI();
}
});
}
}
Вышеуказанная программа может повлиять на видимость, нажав JButton
.
Это может быть проблема с потоками?
Моим следующим подозрением было то, что, возможно, Thread
, которого нет в потоке диспетчеризации событий (EDT) , , возможно, не влияет на отображение немедленно, поэтому я добавил следующее после инициализации JLabel
и JButton
.
Thread t = new Thread(new Runnable() {
public void run() {
while (true) {
b.setVisible(!b.isVisible());
try {
Thread.sleep(100);
} catch (InterruptedException e) { /* Handle exception /* }
}
}
});
t.start();
С новым Thread
он менял переключаемый видимость JLabel
каждые 100 мс, и это также работало без проблем.
Вызов компонента Swing из потока диспетчеризации событий (EDT) - это плохо, так как Swing не является потокобезопасным. Я был немного удивлен, что это сработало, и тот факт, что это работает, может быть просто случайностью .
Перекрасить JPanel
?
Если на видимость JLabel
влияет только изменение размера, это, вероятно, означает, что JLabel
рисуется только тогда, когда JPanel
перерисовывается.
Следует попробовать вызвать метод JPanel
repaint
, чтобы увидеть, изменится ли видимость JLabel
.
Но этот метод, похоже, является просто бинтом для ситуации, если главная причина - поток из EDT пытается внести изменения в GUI. (Как примечание, метод repaint
является поточно-ориентированным, поэтому его можно вызывать потоками вне EDT, но использование repaint
- это обходной путь, а не решение.)
Попробуйте использовать SwingUtilities.invokeLater
Наконец, вероятно, я бы попробовал метод SwingUtilities.invokeLater
, который можно вызывать (и должен вызывать только) из потока, работающего отдельно от EDT, если он хочет повлиять на GUI.
Итак, предыдущий Thread
пример должен быть записан как:
Thread t = new Thread(new Runnable() {
public void run() {
while (true) {
try {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
b.setVisible(!b.isVisible());
}
});
} catch (Exception e1) { /* Handle exception */ }
try {
Thread.sleep(100);
} catch (InterruptedException e) { /* Handle exception */ }
}
}
});
t.start();
Если изменение в графическом интерфейсе действительно происходит в отдельном потоке, то я бы рекомендовал прочитать Урок: параллелизм в Swing из Учебники по Java , чтобы узнать больше информации о том, как написать многопоточный код с хорошим поведением, используя Swing.