2012-08-13 28 views
6

To mój powitalny kod ekranem,Swing ekran powitalny z paska postępu

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 + "..."); 
     } 
    }); 
} 
} 

Z głównego sposobu Ja powołuje tak,

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); 
      } 
     } 
    }); 
} 

w konstruktorze AppFrame nazywam, ekran powitalny .setProgress (msg, val) metoda aktualizacji paska postępu. Ale plusk nie jest wyświetlany. Pokazuje się tylko na końcu, gdy ramka jest wyświetlana tylko przez ułamek sekundy, mimo że ładowanie zajmuje dużo czasu. Ale jeśli wstawię te trzy linie poza invokeLater(), pojawi się ekran powitalny, a pasek postępu ładnie się zaktualizuje. Wierzę, że aktualizacje GUI powinny być w invokeLater. Jaki może być problem?

Przy okazji, AppFrame ładuje różne panele mojej aplikacji.

Edytuj: Podsumowanie mojej aplikacji AppFrame pokazano poniżej.

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

* "Wierzę, że aktualizacje GUI powinny być w invokeLater." * Lub 'invokeAndWait'. Ale zrób trochę rejestrowania, spodziewam się, że znajdziesz czas pomiędzy zakończeniem 'splashScreen.showScreen();' i 'AppFrame frame = new AppFrame (splashScreen);' jest znacznie mniej niż się spodziewasz. Zwróć też uwagę, że najlepsze plamy to stricte AWT (no Swing). –

+0

Oznacza to, że powinienem używać okna lub ramki zamiast JWindow? Dzięki. – Praveen

+0

Tak, użyj 'Window' lub' Frame'. Zajrzyj także do "MediaTracker' /' Canvas' & "EventQueue' - musi być ** no ** Import lub klasa Swing, lub cały pakiet Swing jest ładowany 1. –

Odpowiedz

2

An invokedLater jest wywoływany z innego invokeLater. Model runnable zostanie wykonany dopiero po zakończeniu wykonywania pierwszego.

Należy zmodyfikować kod splasha tak:

... 
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

Wypróbowałem Twoje rozwiązanie. Ponieważ jest już w EDT, w innym przypadku część w 'runInEdt' nie zostanie wykonana. W każdym razie nadal mam ten sam problem. Pasek postępu nie jest aktualizowany w EDT. Użyłem tego jako [przykład] (http://www.devdaily.com/java/java-splash-screen-with-progress-bar-4) i właśnie dodałem invokeLater w main(). Bez invokeLater działa dobrze. – Praveen

+0

Metoda "runInEDT" zapewnia bezpieczeństwo wątku. Można go wywołać z dowolnego wątku. Nie rozumiem, dlaczego moje rozwiązanie nie działa dla Ciebie. Być może możesz podać więcej szczegółów na temat zawartości konstruktora AppFrame. – gontard

+0

Pls zobacz edycję, która ma udawać moją AppFrame. Zasadniczo ładuję całe moje składniki aplikacji w tym konstruktorze. Dlatego chcę zaktualizować pasek postępu podczas ładowania składników. Ale nie wyświetla się, mimo że ładowanie trwa dość długo. – Praveen

4

Hm zapoznać się z tej próbki roboczej I umieścić razem, który wykorzystuje klasa SplashScreen (tylko wykorzystuje prostą Timer i ActionListener wzrost wartości ProgressBar „s aż do 100, a rama jest pokazany)

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(); 
    } 

myślę w ten sposób można utrzymać swoje scrren Splash.