2012-08-13 23 views
6

Questo è il mio codice Splash Screen,battente con barra di avanzamento

public class SplashScreen extends JWindow { 

private static final long serialVersionUID = 1L; 
private BorderLayout borderLayout = new BorderLayout(); 
private JLabel imageLabel = new JLabel(); 
private JProgressBar progressBar = new JProgressBar(0, 100); 

public SplashScreen(ImageIcon imageIcon) { 
    imageLabel.setIcon(imageIcon); 
    setLayout(borderLayout); 
    add(imageLabel, BorderLayout.CENTER); 
    add(progressBar, BorderLayout.SOUTH); 
    pack(); 
    setLocationRelativeTo(null); 
} 

public void showScreen() { 
    SwingUtilities.invokeLater(new Runnable() { 
     public void run() { 
      setVisible(true); 
     } 
    }); 
} 

public void close() { 
    SwingUtilities.invokeLater(new Runnable() { 
     public void run() { 
      setVisible(false); 
      dispose(); 
     } 
    }); 
} 

public void setProgress(final String message, final int progress) { 
    SwingUtilities.invokeLater(new Runnable() { 
     public void run() { 
      progressBar.setValue(progress); 
      if (message == null) { 
       progressBar.setStringPainted(false); 
      } else { 
       progressBar.setStringPainted(true); 
      } 
      progressBar.setString("Loading " + message + "..."); 
     } 
    }); 
} 
} 

Dal metodo principale che sto invocando come questo,

public static void main(String[] args) { 

    SwingUtilities.invokeLater(new Runnable() { 
     public void run() { 
      try { 
       UIManager.setLookAndFeel(UIManager 
         .getSystemLookAndFeelClassName()); 

       SplashScreen splashScreen = new SplashScreen(new ImageIcon("images/splash.jpg")); 
       splashScreen.showScreen(); 

       AppFrame frame = new AppFrame(splashScreen); 

      } catch (Exception e) { 
       appLogger.error(e.getMessage(), e); 
      } 
     } 
    }); 
} 

Nel costruttore di AppFrame io chiamo, splashScreen .setProgress (msg, val) metodo per aggiornare la barra di avanzamento. Ma lo splash non viene mostrato. Viene visualizzato solo alla fine quando il frame viene visualizzato solo per una frazione di secondo, anche se il carico richiede molto tempo. Ma se metto queste tre linee

SplashScreen splashScreen = new SplashScreen(new ImageIcon("images/splash.jpg")); 
splashScreen.showScreen(); 
AppFrame frame = new AppFrame(splashScreen); 

fuori del invokeLater() la schermata di avvio viene visualizzata e gli aggiornamenti barra di avanzamento bene. Credo che gli aggiornamenti della GUI dovrebbero essere in invokeLater. Quale potrebbe essere il problema?

Btw, AppFrame carica vari pannelli della mia applicazione.

Modifica: Un esempio del mio AppFrame è mostrato sotto.

public class AppFrame extends JFrame { 

public AppFrame(SplashScreen splashScreen) { 
    JPanel test = new JPanel(); 
    test.setLayout(new GridLayout(0, 10)); 

    splashScreen.setProgress("jlabel", 10); 
    for(int i = 0; i < 10000; i++) { 
     test.add(new JButton("Hi..." + i)); 
     splashScreen.setProgress("jbutton", (int)(i * 0.1)); 
    } 
    add(new JScrollPane(test)); 
    setPreferredSize(new Dimension(800, 600)); 
    pack(); 
    setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); 
    setLocationRelativeTo(null); 
    splashScreen.setProgress("complete", 100); 
    setVisible(true); 
} 
} 
+1

* "Credo che gli aggiornamenti della GUI dovrebbero essere in invokeLater." * O 'invokeAndWait'. Ma faccio un po 'di logging, mi aspetto che troverete gli orari tra il completamento di 'splashScreen.showScreen();' e 'AppFrame frame = new AppFrame (splashScreen);' è molto meno di quanto ci si aspetti. Si noti inoltre che i migliori schizzi sono rigorosamente AWT (no Swing). –

+0

Ciò significa che dovrei usare Window o Frame invece di JWindow? Grazie. – Praveen

+0

Sì, usa 'Finestra' o' Cornice'. Guarda anche in 'MediaTracker' /' Canvas' e 'EventQueue' - ci devono essere ** no ** Swing import o classi usate, o l'intero pacchetto Swing viene caricato prima. –

risposta

2

Un invokedLater viene richiamato da un altro invokeLater. Il runnable verrà eseguito solo quando l'esecuzione del primo è terminata.

è necessario modificare il codice Splashscreen come questo:

... 
private void runInEdt(final Runnable runnable) { 
    if (SwingUtilities.isEventDispatchThread()) 
     runnable.run(); 
    else 
     SwingUtilities.invokeLater(runnable); 
} 

public void showScreen() { 
    runInEdt(new Runnable() { 
     public void run() { 
      setVisible(true); 
     } 
    }); 
} 

public void close() { 
    runInEdt(new Runnable() { 
     public void run() { 
      setVisible(false); 
      dispose(); 
     } 
    }); 
} 

public void setProgress(final String message, final int progress) { 
    runInEdt(new Runnable() { 
     public void run() { 
      progressBar.setValue(progress); 
      if (message == null) 
       progressBar.setStringPainted(false); 
      else 
       progressBar.setStringPainted(true); 
      progressBar.setString("Loading " + message + "..."); 
     } 
    }); 
} 
+0

Ho provato la tua soluzione. Dal momento che è già in EDT, altrimenti parte in 'runInEdt' non verrà eseguita. Ad ogni modo ho ancora lo stesso problema. La barra di avanzamento non viene aggiornata in EDT. L'ho usato come [esempio] (http://www.devdaily.com/java/java-splash-screen-with-progress-bar-4) e ho appena aggiunto invokeLater in main(). Senza invokeLater funziona correttamente. – Praveen

+0

Il metodo 'runInEDT' garantisce che il tuo Splashscreen sia thread-safe. Può essere invocato da qualsiasi thread. Non capisco perché la mia soluzione non funzioni per te. Potrebbe essere possibile fornire ulteriori dettagli sul contenuto del costruttore AppFrame. – gontard

+0

Per vedere la modifica che ha una simulazione del mio AppFrame. Fondamentalmente sto caricando i miei componenti dell'intera app in quel costruttore. Quindi voglio aggiornare la barra di avanzamento mentre i componenti caricano. Ma non sta mostrando anche se ci vuole un bel po 'di tempo per caricare. – Praveen

4

Hmm un'occhiata a questo esempio reale metto insieme che utilizza la classe SplashScreen (utilizza solo un semplice Timer e ActionListener aumentare il valore s del ProgressBar' fino 100 ed è mostrato un telaio):

import java.awt.BorderLayout; 
import java.awt.Container; 
import java.awt.Font; 
import java.awt.event.ActionListener; 
import javax.swing.*; 

public class SplashScreen extends JWindow { 

    private static JProgressBar progressBar = new JProgressBar(); 
    private static SplashScreen splashScreen; 
    private static int count = 1, TIMER_PAUSE = 25,PROGBAR_MAX=100; 
    private static Timer progressBarTimer; 
    ActionListener al = new ActionListener() { 

     @Override 
     public void actionPerformed(java.awt.event.ActionEvent evt) { 
      progressBar.setValue(count); 
      System.out.println(count); 
      if (PROGBAR_MAX == count) { 
       splashScreen.dispose();//dispose of splashscreen 
       progressBarTimer.stop();//stop the timer 
       createAndShowFrame(); 
      } 
      count++;//increase counter 

     } 
    }; 

    public SplashScreen() { 
     createSplash(); 
    } 

    private void createSplash() { 
     Container container = getContentPane(); 

     JPanel panel = new JPanel(); 
     panel.setBorder(new javax.swing.border.EtchedBorder()); 
     container.add(panel, BorderLayout.CENTER); 

     JLabel label = new JLabel("Hello World!"); 
     label.setFont(new Font("Verdana", Font.BOLD, 14)); 
     panel.add(label); 

     progressBar.setMaximum(PROGBAR_MAX); 
     container.add(progressBar, BorderLayout.SOUTH); 


     pack(); 
     setLocationRelativeTo(null); 
     setVisible(true); 

     startProgressBar(); 
    } 

    private void startProgressBar() { 
     progressBarTimer = new Timer(TIMER_PAUSE, al); 
     progressBarTimer.start(); 
    } 

    private void createAndShowFrame() { 
     JFrame frame = new JFrame(); 
     frame.setSize(500, 500); 
     frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
     frame.setVisible(true); 
    } 

    public static void main(String[] args) { 
     SwingUtilities.invokeLater(new Runnable() { 

      @Override 
      public void run() { 
       splashScreen = new SplashScreen(); 
      } 
     }); 
    } 
} 
0
Thread l_splash_thread = new Thread(
      new SplashScreen()); 
    l_splash_thread.start(); 

    try { 
     l_splash_thread.join(); 
    } catch (InterruptedException e1) { 
     e1.printStackTrace(); 
    } 

penso che in questo modo è possibile tenere le scrren-schizzo.

Problemi correlati