2015-01-22 8 views
0

Моя проблема, похоже, связана с тем, как Swing использует потоки внутри. Я хочу показать индикатор занятости, когда приложение выполняет трудоемкую задачу, которую я называю Spinner. У меня есть Swing JDialog и адаптирован код от Oracle TutorialJava Swing показывает BusyIndicator во время трудоемкой задачи

Как исправить проблему?

У меня есть startSpinner() - и метод stopSpinner(). Они работают отлично. У меня есть 2 JButtons, которые показывают или скрывают его. Но проблема в том, что когда я вызываю метод startSpinner(), при выполнении задачи, которая работает около 5 секунд, счетчик не отображается. Думаю, я должен иметь дело с SwingUtilities.invokeLater. Но у меня нет опыта с этим. Это мой метод для запуска дисплея счетчика.

StartSpinner метод:

private void startSpinner() { 
    SwingUtilities.invokeLater(new Runnable() { 
     @Override 
     public void run() { 
      layerUI.start(); 
      if (!stopper.isRunning()) { 
       stopper.start(); 
       setTitle("Working..."); 
      } 
     } 
    }); 
} 

Стоп Spinner метод:

public void stopSpinner() { 
    logger.info("stopSpinner"); 
    layerUI.stop(); 
} 

WaitLayerUI класс:

package view; 

import java.awt.AlphaComposite; 
import java.awt.BasicStroke; 
import java.awt.Color; 
import java.awt.Composite; 
import java.awt.Graphics; 
import java.awt.Graphics2D; 
import java.awt.RenderingHints; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.beans.PropertyChangeEvent; 

import javax.swing.JComponent; 
import javax.swing.JLayer; 
import javax.swing.JPanel; 
import javax.swing.Timer; 
import javax.swing.plaf.LayerUI; 

@SuppressWarnings("serial") 
class WaitLayerUI extends LayerUI<JPanel> implements ActionListener { 
private boolean mIsRunning; 
private boolean mIsFadingOut; 
private Timer mTimer; 

private int mAngle; 
private int mFadeCount; 
private int mFadeLimit = 15; 

@Override 
public void paint(Graphics g, JComponent c) { 
    int w = c.getWidth(); 
    int h = c.getHeight(); 

    // Paint the view. 
    super.paint(g, c); 

    if (!mIsRunning) { 
     return; 
    } 

    Graphics2D g2 = (Graphics2D) g.create(); 

    float fade = (float) mFadeCount/(float) mFadeLimit; 
    // Gray it out. 
    Composite urComposite = g2.getComposite(); 
    g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, .5f * fade)); 
    g2.fillRect(0, 0, w, h); 
    g2.setComposite(urComposite); 

    // Paint the wait indicator. 
    int s = Math.min(w, h)/5; 
    int cx = w/2; 
    int cy = h/2; 
    g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); 
    g2.setStroke(new BasicStroke(s/4, BasicStroke.CAP_ROUND, BasicStroke.JOIN_ROUND)); 
    g2.setPaint(Color.white); 
    g2.rotate(Math.PI * mAngle/180, cx, cy); 
    for (int i = 0; i < 12; i++) { 
     float scale = (11.0f - i)/11.0f; 
     g2.drawLine(cx + s, cy, cx + s * 2, cy); 
     g2.rotate(-Math.PI/6, cx, cy); 
     g2.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, scale * fade)); 
    } 

    g2.dispose(); 
} 

@Override 
public void actionPerformed(ActionEvent e) { 
    if (mIsRunning) { 
     firePropertyChange("tick", 0, 1); 
     mAngle += 3; 
     if (mAngle >= 360) { 
      mAngle = 0; 
     } 
     if (mIsFadingOut) { 
      if (--mFadeCount == 0) { 
       mIsRunning = false; 
       mTimer.stop(); 
      } 
     } else if (mFadeCount < mFadeLimit) { 
      mFadeCount++; 
     } 
    } 
} 

public void start() { 
    if (mIsRunning) { 
     return; 
    } 

    // Run a thread for animation. 
    mIsRunning = true; 
    mIsFadingOut = false; 
    mFadeCount = 0; 
    int fps = 24; 
    int tick = 1000/fps; 
    mTimer = new Timer(tick, this); 
    mTimer.start(); 
} 

public void stop() { 
    mIsFadingOut = true; 
} 

@Override 
public void applyPropertyChange(PropertyChangeEvent pce, JLayer l) { 
    if ("tick".equals(pce.getPropertyName())) { 
     l.repaint(); 
    } 
} 
} 

Я использую код из учебника, кроме основного метода, может быть, это и есть причина проблемы. Решение должно быть таким:

private void save(){ 
startSpinner(); 
performTimeConsumingSave(); 
stopSpinner(); 
} 

В настоящее время счетчик не появляется вообще. Но это проявляется, когда я показываю JOptionPane (который приостанавливает текущий поток). Я также попробовал Thread.sleep (100), но это была не очень хорошая идея.

+1

Я хотел бы убедиться performTimeConsumingSave не выполняется на EDT. Возможно, ваш прядильщик настроен на запуск, но не делает ничего в течение длительного времени, потому что ваше действие также находится на EDT, а затем сразу заканчивается, как сразу же вызывается stopSpinner(). Кроме того, stopSpinner() кажется странным, единственное, что он делает, это logs message? – NESPowerGlove

+1

Посмотрите на http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html – Michal

+0

Ах, простите, @ NESPowerGlove, я слишком много стирал, в то время как я сокращал код. Он вызывает метод stop() и останавливает таймер. –

ответ

1

Благодаря комментарию Михала и этому сообщению How do I use SwingWorker in Java? Я смог решить проблему. Потенциально продолжительный метод выполненияTimeConsumingSave() теперь вызывается SwingWorker. Мне понравилось пособие. Таким образом, метод SwingWorkers doInBackground() теперь вызывается из метода ActionPerformed ActionListener. Вместо вызова функции save() непосредственно из EDT вызов save() выполняется с помощью SwingWorker. Чтобы определить, было ли сохранение успешным, я проверяю retval. Теперь появляется счетчик, и система выполняет долговременную задачу (save()) одновременно. Вот код PostWorker:

class PostWorker extends SwingWorker<Integer, Integer> { 
    @Override 
    protected Integer doInBackground() throws Exception { 
     // Do a time-consuming task. 
     starteSpinner(); 
     int status = save(); 
     stopSpinner(); 
     return status; 
    } 


    @Override 
    protected void done() { 
     try { 
      if (get()== SAVE_OK) { 
       JOptionPane.showMessageDialog(PostAusDetailDialog.this, "Mail saved."); 
      } 
     } catch (Exception e) { 
      e.printStackTrace(); 
     } 
    } 
}