2012-04-29 4 views
1

У меня есть приложение с:Swing: jLabel.setText вызывалась слишком поздно

  • один JTextField для пользовательского ввода,
  • один JLabel сюда, показывающий состояние занятости,
  • один JTextArea для печати результатов поиска.

Я хочу, чтобы пользователь написал текст в текстовое поле, нажмите enter и просмотрите результаты. У меня есть слушатель, как это:

private void searchForPattern(java.awt.event.ActionEvent evt) { 
     textArea.setText(""); 
     busyLabel.setText("Searchnig ..."); 
     doSearch(); 
     busyLabel.setText("Idle"); 
    } 

В doSearch существует довольно сложный алгоритм, который открывает много файлов XML и поиск по заданному шаблону, это занимает некоторое время. Текст busyLabel изменен на поиск ... только после завершения doSearch. В doSearch нет второго потока, только много операций ввода-вывода.

Как это исправить?

ответ

4

У вас есть классическая проблема параллелизма Swing (учебник: Concurrency in Swing), где doSearch связывает поток событий Swing. Поскольку этот поток - это то, где Swing выполняет всю свою рисование/рисование и взаимодействует с пользователями, если он привязан кодом, который требует сколько-нибудь заметного количества времени, все приложение «зависает» без обновления компонентов и взаимодействия с пользователем игнорируется.

Решение: сделайте это на фоновом потоке, таком как предоставленный объектом SwingWorker. Установите строку «Idle» в JLabel в методе SwingWorker done().

т.е.

private void searchForPattern(java.awt.event.ActionEvent evt) { 
    textArea.setText(""); 
    busyLabel.setText("Searching ..."); 
    new SwingWorker<Void, Void>() { 
    @Override 
    protected Void doInBackground() throws Exception { 
     doSearch(); 
     return null; 
    } 

    @Override 
    protected void done() { 
     busyLabel.setText("Idle"); 
    } 
    }.execute(); 
} 
+0

OK большой, это помогло. – Xorty

+0

@ Xorty: добро пожаловать. Будьте уверены, хотя прочитайте учебник, поскольку он содержит важную информацию, если вы собираетесь использовать любую кодировку Swing. –

+0

Thanx, да, я действительно не планирую кодировать Swing, но я определенно прочитал какую-нибудь документацию, чтобы понять, как работает swing, если мне пришлось чаще использовать Swing. – Xorty

1

Вы также можете использовать invokeLater метод в соответствии с SwingUtilities, чтобы обновить компоненты GUI распашные из потоке приложения.

private void searchForPattern(java.awt.event.ActionEvent evt) { 

    SwingUtilities.invokeLater(
    new Runnable(){ 
     public void run(){   
     textArea.setText(""); 
     busyLabel.setText("Searchnig ..."); 
     } 
    } 
); 

    doSearch(); 

    SwingUtilities.invokeLater(
    new Runnable(){ 
     public void run(){ 
     busyLabel.setText("Idle"); 
     } 
    } 
); 
} 

EDIT PS: здесь я предполагаю, что вы не называется функцией searchForPattern() из EDT, что это значит, если вы называете это от ActionListener вы должны назвать так:

new Thread(
    new Runnable(){ 
     public void run(){ 
     searchForPattern(....) 
     } 
    } 
).start(); 
+0

Этот код, вероятно, не подходит для текущей проблемы OP. Существует вероятность> 95%, что метод 'searchForPattern (...)' вызывается в EDT и на самом деле выглядит вызываемым из ActionListener JButton с учетом его параметра ActionEvent и описания проблемы оригинального плаката. Если это так, то ваше предложение приведет к вызову 'doSearch()' на EDT, что точно не должно быть сделано. –

+0

Вы имеете право забыть упомянуть, что он должен вызвать метод 'searchForPattern (...)' в новом потоке, если он вызвал его из ActionListener. –

+0

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

Смежные вопросы