Мой JLabel не обновляется быстро. Почему?

Я читаю носитель с vlcj и хочу отображать прошедшее и оставшееся время в некоторых JLabels. Я написал некоторый код, но кажется, что мой JLabel.setText не обновляется чаще, чем 2 раза в секунду.

Чтобы сделать еще одну попытку и быть уверенным, что это не поток vlcj, у которого будут проблемы, я написал очень код с JLabel. Цель этого простого кода — обновлять JLabel 10 раз в секунду.

Вот мой код:

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

import javax.swing.JFrame;
import javax.swing.JLabel;

public class TestLabel extends JFrame implements Runnable{
JLabel label = new JLabel("0");
int i=0;
TestLabel() {
    this.setTitle("Test");
    this.setSize(200, 200);
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    this.setContentPane(label);
    this.setVisible(true);      
}

public static void main(String[] args) {
    TestLabel tLabel = new TestLabel();
    Thread t1 = new Thread(tLabel);
    t1.start();
}

@Override
public void run() {
    ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
    scheduler.scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
            i+=1;
            System.out.println(i);
            label.setText(String.valueOf(i));               
        }           
    }, 0, 100, TimeUnit.MILLISECONDS);      
}
}

Результат: в консоли я получаю 1, 2, 3, 4... Но в JLabel у меня что-то вроде: 1, 2, 3 (...) 32, 37, 42, 47. Кажется, что System.out.println записывает каждую букву «i», а JLabel — нет. Зачем мне этот артефакт?

Спасибо за все ваши ответы. С уважением.


person Dr_Click    schedule 10.01.2016    source источник


Ответы (3)


Вы не должны использовать компоненты Swing из потока, отличного от потока диспетчеризации событий.

Поэтому либо используйте Swing Timer, чем ScheduledExecutorService или оберните изменение метки в SwingUtilities.invokeLater().

Кстати, вызов new TestLabel(); также должен быть заключен в SwingUtilities.invokeLater(). Прочтите руководство по параллелизму Swing.

person JB Nizet    schedule 10.01.2016
comment
Проголосовать за урок английского - person Idos; 10.01.2016

Вам нужно вызвать метод SwingUtilities.invokeLater, чтобы правильно обновить текст графического интерфейса при использовании Swing (т.е. JFrame, JLabel и т. д.).

public void run() {
    i+=1;
    System.out.println(i);
    SwingUtilities.invokeLater(new Runnable() {
        public void run() {
            label.setText(String.valueOf(i));               
        }           
    }); 
}

Для получения дополнительной информации о SwingUtilities.invokeLater ознакомьтесь с этой публикацией SO.

person hsirkar    schedule 10.01.2016

Не используйте ScheduledExecutorService. Компоненты Swing необходимо обновить в версии Event Dispatch Thread (EDT).

Вместо этого вы должны использовать Swing Timer. Прочтите раздел руководства по Swing, посвященный Как использовать таймеры. Чтобы получить больше информации.

Вот простой пример использования таймера: >Как сделать так, чтобы JScrollPane (в BorderLayout, содержащий JPanel) плавно автопрокручивался. Просто измените интервал на 100 вместо 1000.

person camickr    schedule 10.01.2016