Java Swing: перекрасить компонент во время длинного события - PullRequest
2 голосов
/ 19 мая 2011

Я работаю с Java Swing, чтобы визуализировать мой графический интерфейс для приложения.У меня есть ActionListener, настроенный на JButton для запуска серии тестов.В идеале я хотел бы обновить область состояния (в настоящее время JTextArea) с информацией и результатами тестирования в режиме реального времени.Другими словами, программа будет устанавливать текстовую область построчно, когда тесты запускаются и завершаются, например:

Running Tests... 
    Test 1:  Check Something...
         Success
    Test 2:  Check Something else...
         Success
    ..    //More testing output

All tests have completed successfully.  Yay.

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

Я пытался вызывать repaint на моем JTextArea каждый раз, когда обновляюсьстатус, но кажется, что он просто добавляется в очередь событий (которая не будет запущена до тех пор, пока мое actionPerformed событие не завершится ... до).Я также пытался вызвать revalidate и updateUI и даже пытался вызвать wait() в классе ActionListener, в котором выполнялись мои тесты, но пока ничего не получалось.Вот структура моего текущего кода:

..

JTextArea statusOutput_textArea;

..

public void actionPerformed(ActionEvent e) {
    setStatusText("Running Tests...");

    //Run Tests
    int currentTest = 1;

    //Check something
    appendStatusText("        Test " + currentTest++ + ":  Checking Something...");
    ..    //Check Something Here
    appendStatusText("            Success");

    //Check something else
    appendStatusText("        Test " + currentTest++ + ":  Checking Something else...");
    ..    //Check Something Else Here
    appendStatusText("            Success");

    //Other tests
    ..    //Run other tests here

    appendStatusText("\nAll tests have completed successfully.  Yay.");
}//End of actionPerformed method

public void setStatusText (String statusText) {
    statusOutput_textArea.setText(statusText);
    statusOutput_textArea.repaint();
}//End of setStatusText method

public void appendStatusText (String statusTextToAppend) {
    statusOutput_textArea.setText(statusOutput_textArea.getText() + "\n" + statusTextToAppend);
    statusOutput_textArea.repaint();
}//End of appendStatusText method 

Любая помощь будет высоко ценится:)

ОБНОВЛЕНИЕ

Для тех, кто заинтересован вОбщая структура решения, вот она:

public class RunTestsButtonActionListener implements ActionListener {
    JTextArea statusOutput_textArea;
    JButton testDatabaseSettings_JButton;

    public RunTestsButtonActionListener(JTextArea statusOutput_textArea){
        this.statusOutput_textArea = statusOutput_textArea;
    }

    public void actionPerformed(ActionEvent e) {
        testDatabaseSettings_JButton = (JButton) e.getSource();

        Thread tests = new Thread(){
            public void run() {
                //Disable button and add tooltip
                testDatabaseSettings_JButton.setEnabled(false);
                testDatabaseSettings_JButton.setToolTipText("Running Tests...");

                //Run Tests
                try {
                    statusOutput_textArea.setText("Running Tests...");
                    int currentTest = 1;

                    //Check something
                    statusOutput_textArea.append("\n        Test " + currentTest++ + ":  Checking Something...");
                    ..    //Check Something Here
                    statusOutput_textArea.append("\n            Success");

                    //Check something else
                    statusOutput_textArea.append("\n        Test " + currentTest++ + ":  Checking Something else...");
                    ..    //Check Something Else Here
                    statusOutput_textArea.append("\n            Success");

                    //Other tests
                    ..    //Run other tests here

                    statusOutput_textArea.append("\n\nAll tests have completed successfully.  Yay.");
                } finally {
                    //Enable button and remove tooltip
                    testDatabaseSettings_JButton.setEnabled(false);
                    testDatabaseSettings_JButton.setToolTipText("");
                }
            }
        };

        tests.start();
    }
}

Ответы [ 3 ]

2 голосов
/ 19 мая 2011

Вы выполняете проверки в "потоке Swing", вам будет лучше выполнять эти задачи в другом потоке (который уведомляет GUI, если что-то происходит / изменяется).

1 голос
/ 19 мая 2011

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

Вам следует попробовать взглянуть на SwingWorker или какой-либо другой фоновый поток. Вы будете запускать свои тесты в фоновом потоке и каждый раз, когда захотите обновить пользовательский интерфейс, вы будете вызывать:

SwingUtils.invokeLater(new Runnable() {
     public void run() {
        appendStatusText(....);
    }
});
1 голос
/ 19 мая 2011

Запускайте каждый тест в отдельном потоке, чтобы они не блокировали друг друга.Кроме того, JTextArea уже имеет поточно-ориентированный метод append, нет необходимости использовать setText для симуляции append.

...