(Java) Появляющаяся / исчезающая JLabel в JPanel исчезает только при изменении размера
Вопрос
Я работаю на Java, и у меня есть JPanel в JFrame.В этой JPanel, среди прочего, у меня есть JLabel, который я хочу заставлять появляться и исчезать по своему желанию.Я пробовал устанавливать для видимости значение true / false, добавляя и удаляя его из JFrame и JPanel, и, посмотрев онлайн, я пробовал validate()ing и revalidate ()ing до бесконечности.Что здесь можно сделать, чтобы решить эту проблему?
Решение
В общем, называя 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
's repaint
метод, позволяющий проверить, является ли видимость JLabel
изменится.
Но этот метод, похоже, является всего лишь вспомогательным средством в ситуации, если основная причина связана с тем, что поток, отключенный от EDT, пытается внести изменения в графический интерфейс.(Просто в качестве примечания, repaint
метод потокобезопасен, поэтому он может быть вызван внешними потоками, но полагается на repaint
это скорее обходной путь, чем решение.)
Попробуйте использовать SwingUtilities.invokeLater
Наконец, вероятно, то, что я бы попробовал, - это SwingUtilities.invokeLater
метод, который может быть вызван (и должен быть вызван только) из потока, выполняемого отдельно от EDT, если он хочет повлиять на графический интерфейс пользователя.
Итак, чем раньше 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.
Другие советы
setVisible() или его удаление должно работать нормально, однако убедитесь, что вы делаете это из потока отправки событий.В EventQueue есть служебные методы для запуска блоков в этом потоке.
http://helpdesk.objects.com.au/java/how-do-i-update-my-gui-from-any-thread
Вам нужно было бы вызвать revalidate() в родительской JPanel, если вам нужно, чтобы ее компоненты были повторно размещены.
Если вы можете опубликовать пример, демонстрирующий проблему, я могу взглянуть на него для вас.