2016-09-14 66 views
3

要在這裏解釋一下我的問題是MCVE這裏一下[點擊JButtonJDialog A打開JDialog B:漫長的過程:意外延遲

import java.awt.BorderLayout; 
import java.awt.Color; 
import java.awt.FlowLayout; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 

import javax.swing.JButton; 
import javax.swing.JDialog; 

public class DiagA extends JDialog { 

    private DiagB diag; 

    public DiagA() { 

     super(); 
     setTitle("main diag"); 
     setSize(200, 150); 
     setLocation(400,400); 

     JButton btn = new JButton("Show DiagB"); 
     btn.addActionListener(new ActionListener() { 
      @Override 
      public void actionPerformed(ActionEvent arg0) { 

       showDiag(); 
      } 
     }); 
     add(btn, BorderLayout.NORTH); 

     //make main frame visible 
     setVisible(true); 
    } 

    void showDiag() { 

     if(diag == null) { 

      diag = new DiagB(); 

      //this prints out as expected 
      System.out.println("set visible done"); 

      try { 
       Thread.sleep(3000); 
      } catch (InterruptedException ex) {} 

      //only after the delay diag shows in full 
     } 
    } 

    public static void main(String[] args) { 
     new DiagA(); 
    } 
} 

class DiagB extends JDialog { 

    public DiagB() { 

     super(); 
     setTitle("2nd diag"); 
     setSize(150, 100); 
     setLocation(600,420); 
     setLayout(new FlowLayout(FlowLayout.CENTER)); 
     getContentPane().setBackground(Color.YELLOW); 
     setVisible(true); 
    } 
} 

正如你可以在代碼中看到我添加了一個3秒延遲創建後DiagB。 點擊該按鈕DiagB這樣表示:

enter image description here

的3秒延時結束後唯一,DiagB顯示全:

enter image description here

我的問題是:
一個。爲什麼DiagB在構建後沒有完全顯示? (僅當showDiag()返回時才顯示全部)。 b。我的問題的原因是DiagB需要更新,在DiagA長進程。
什麼是更新的正確方法?是否需要在每個更新過程中使用SwingWorker

+1

如果它是顯示而不是用戶交互,它不應該是一個'JDialog'這是,嗯,與用戶對話。在事件處理程序中,應該永遠不會進行睡眠(或任何其他長操作)。 – RealSkeptic

+1

@RealSkeptic我明白你說的關於事件處理程序長時間操作的。('JDialog'或'JFrame'不會讓這種情況下太大的差別。一個'可能需要JDialog'對於形態爲例)。 – c0der

+1

模態意味着用戶交互是立即需要的。否則,它的使用不正確。 – RealSkeptic

回答

4

一個。 showDiag在GUI線程上運行。當GUI線程休眠時,GUI將完全死亡。

b。是的,使用SwingWorker對於長時間運行的任務和使用SwingUtilities.invokeLater()提交GUI的更新任務回到GUI線程。或者,實現SwingWorker#done()這是在SwingWorker任務完成後在GUI線程上運行的便捷方法。

+0

謝謝。我明白你的意思,「稍後安排行動」。在每個更新過程中使用「SwingWorker」都很麻煩。我寧願在更新的('DiagB')對象中實現它。有什麼建議? – c0der

+1

@MarkoTopolnik注意OP使用的睡眠*** ***模擬長時間運行的任務,所以他們一**'SwingWorker'的建議**向下靠近帖子底部似乎是最好的策略這一點。 –

+2

@AndrewThompson對,我更新推薦'SwingWorker' +'invokeLater'。 –

0

基於馬爾科Topolnik答案,安德魯·湯普森評論,我扭曲了SwingWorker漫長的過程。
這工作得很好:

import java.awt.BorderLayout; 
import java.awt.Color; 
import java.awt.FlowLayout; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 

import javax.swing.JButton; 
import javax.swing.JDialog; 
import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.SwingWorker; 

public class DiagA extends JDialog { 

    private FrameB frame; 
    private JButton btn; 

    public DiagA() { 

     super(); 
     setTitle("main frame"); 
     setSize(200, 150); 
     setLocation(400,400); 

     btn = new JButton("Show Frame B"); 
     btn.addActionListener(new ActionListener() { 
      @Override 
      public void actionPerformed(ActionEvent arg0) { 

       show2ndFrame(); 
      } 
     }); 
     add(btn, BorderLayout.NORTH); 
     setVisible(true); 
    } 

    void show2ndFrame() { 

     if(frame == null) { 

      frame = new FrameB(); 
      btn.setText("Exit"); 

     }else { 

      System.exit(0); 
     } 

     doWork(); 
    } 

    private void doWork() { 

     SwingWorker<Void, Void> sw = new SwingWorker<Void, Void>() { 

      @Override 
      protected Void doInBackground() throws Exception { 

       try { 

        for(int i = 1 ; i<=100 ; i++) { 
         //represents a long process 
         Thread.sleep(100); 
         frame.update(i); 
        } 

       } catch (InterruptedException ex) {} 
       return null; 
      } 
     }; 
     sw.execute(); 
    } 

    public static void main(String[] args) { 
     new DiagA(); 
    } 
} 

class FrameB extends JFrame { 

    JLabel label; 

    public FrameB() { 

     super(); 
     setTitle("2nd frame"); 
     setSize(150, 100); 
     setLocation(600,420); 
     setLayout(new FlowLayout(FlowLayout.CENTER)); 
     getContentPane().setBackground(Color.YELLOW); 
     label = new JLabel("0"); 
     add(label); 
     setVisible(true); 
    } 

    void update(int progress) { 

     label.setText(String.valueOf(progress)); 
    } 
}