Как обновить графический интерфейс Swing из длинного метода?

Я новичок в Swing и в настоящее время пытаюсь разработать простое приложение с графическим интерфейсом в NetBeans.

Я хочу создать какую-то систему ведения журнала с графическим интерфейсом, чтобы записывать текущее действие, выполняемое приложением, в TextArea.

В качестве простого примера я создал форму JFrame, которая содержит только 2 объекта: кнопку «Пуск» и TextArea.

Когда нажата кнопка «Пуск», он вызывает какой-то длительный метод, который должен занять некоторое время (скажем, 10 секунд) для завершения работы, и пока этот метод работает, я хочу добавить текст в TextArea из этого длительный метод (и, конечно, я хочу, чтобы TextArea немедленно обновлялся).

Моя проблема в том, что я не могу найти правильный способ сделать это. Во всяком случае, я пытался это сделать, когда я нажимаю кнопку «Пуск», приложение зависает на 10 секунд, не обновляя TextArea, как я хотел. Только когда метод завершается, я вижу обновление TextArea.

Вот пример кода:

private void startButtonActionPerformed(java.awt.event.ActionEvent evt) {
    try {
        for (int i = 0; i < 10; i++) {
           textArea.setText(i + "\n");
            Thread.sleep(1000);
        }
    } catch (Exception e) {}
}

В этом примере я ожидаю увидеть, что как только я нажму кнопку, в течение следующих 10 секунд новая строка будет добавляться к TextArea каждую секунду, например:

1

2

3

4

...

Но реальный результат, который я получаю от этого кода, заключается в том, что приложение зависает на 10 секунд, и, наконец, TextArea обновляется и отображает только цифру 9.

Я испробовал много различных способов сделать это правильно, в основном используя методы SwingUtilities.invokeLater и SwingWorker, но ни один из них не работал у меня.

Любая помощь в поиске правильного способа сделать это будет принята с благодарностью.


person Rony    schedule 05.08.2012    source источник
comment
Что касается "I've tried many different methods of doing this right, mainly using the SwingUtilities.invokeLater and SwingWorker methods, but none of them worked for me." -- невозможно понять, что вы делаете не так, если вы не показываете попытки. SwingWorker сработает в этой ситуации, так что покажите нам, как вы пытались его использовать, и у нас будет больше шансов помочь вам.   -  person Hovercraft Full Of Eels    schedule 05.08.2012


Ответы (3)


Как указано в других ответах, вы захотите использовать SwingWorker - я рекомендую прочитать об этом, так как это невероятно полезный инструмент. Эта реализация должна дать вам то, что вы ищете в своем образце:

Действие вашей кнопки:

button.addActionListener(new ActionListener(){
    @Override
    public void actionPerformed(ActionEvent arg0) {
        new Worker().execute();
    }
});

Ваш свинг-работник:

public class Worker extends SwingWorker<String, String>{

    @Override
    protected String doInBackground() throws Exception {
        //This is what's called in the .execute method
        for(int i = 0; i < 10; i++){
            //This sends the results to the .process method
            publish(String.valueOf(i));
            Thread.sleep(1000);
        }
        return null;
    }

    protected void process(List<String> item) {
        //This updates the UI
        textArea.append(item + "\n");
    }
}
person Nick Rippe    schedule 06.08.2012
comment
Большое тебе спасибо! Это именно то, что мне нужно. Работает отлично :) - person Rony; 06.08.2012

1. Всегда обеспечивайте работу пользовательского интерфейса в потоке пользовательского интерфейса, а работу без пользовательского интерфейса — в потоке без пользовательского интерфейса.

2. В Java-приложениях с графическим интерфейсом метод main() недолговечен, после планирования построения графического интерфейса в Event Dispatcher Thread метод main() завершает работу... и теперь его EDT несет ответственность за работу с графическим интерфейсом.

3. Всегда сохраняйте поток EDT, который является потоком графического интерфейса только для работы с графическим интерфейсом.

Например:

public static void main(String[] args){


        EventQueue.invokeLater(new Runnable(){

              public void run(){

                myframe.setVisible(true);

             }

         }

  }

4. Создайте отдельную Non-UI цепочку для обработки этого долговременного метода.

5. Вы можете просто использовать Thread или использовать SwingWorker, который специально введен в Java для синхронизации потока пользовательского интерфейса и потока, отличного от пользовательского интерфейса.

person Kumar Vivek Mitra    schedule 05.08.2012
comment
Большое спасибо! Я ценю вашу помощь. - person Rony; 06.08.2012

Используйте класс javax.swing.Timer. Дополнительные сведения см. в разделе Как использовать таймеры Swing.

person mre    schedule 05.08.2012