2015-01-22 3 views
0

Swing이 내부적으로 스레드를 사용하는 방식에서 내 문제가 발생하는 것으로 보입니다. 응용 프로그램이 Spinner라고 부르는 시간이 많이 걸리는 작업을 수행 할 때 Busy Indicator를 보여주고 싶습니다. Swing JDialog를 사용하여 코드를 변경했습니다. Oracle TutorialJava Swing은 시간이 많이 걸리는 작업 중에 BusyIndicator를 표시합니다.

어떻게 수정합니까?

startSpinner() - 및 stopSpinner() - 메서드가 있습니다. 그들은 완벽하게 작동하고 있습니다. 2 개의 JButton을 표시하거나 숨 깁니다. 하지만 문제는 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..."); 
      } 
     } 
    }); 
} 

정지 스피너 방법 :

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에서 실행되어 있지 않은지 확인합니다 : 다음은 PostWorker 코드입니다. 어쩌면 당신의 회 전자가 시작되도록 설정되어 있지만, 당신의 행동이 EDT에 있기 때문에 아무 것도하지 않을 것입니다. 그리고 바로 stopSpinner()가 호출 된 직후에 끝납니다. 또한 stopSpinner()는 이상하게 보입니다. 메시지를 기록하는 것이 유일한 방법일까요? – NESPowerGlove

+1

http://docs.oracle.com/javase/tutorial/uiswing/concurrency/worker.html을보십시오 – Michal

+0

아, 죄송합니다 @ NESPowerGlove 제가 코드를 줄이는 동안 너무 많이 지워 버렸습니다. stop() 메서드를 호출하고 타이머를 중지합니다. –

답변

1

Michal의 의견 및이 게시물 How do I use SwingWorker in Java? 덕분에 문제를 해결할 수있었습니다. potenitally long running 메서드 인 performTimeConsumingSave()가 이제 SwingWorker에 의해 호출됩니다. 나는 자습서가 좋았어요. 따라서 SwingWorkers 메서드 doInBackground()가 ActionListener의 actionPerformed 메서드에서 호출됩니다. EDT에서 직접 save()를 호출하는 대신 save() 호출은 SwingWorker에 의해 수행됩니다. 저장이 완료되었는지 여부를 확인하려면 retval을 검사하고 있습니다. 이제 회 전자가 나타나고 시스템은 오랜 시간이 걸리는 작업 (save())을 동시에 수행합니다.

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(); 
     } 
    } 
} 
관련 문제