2012-09-24 15 views
12

Ciao Sto scrivendo un'applicazione GUI su Java 1.6 con Swing.Visualizzazione dell'animazione Gif in java

Ho uno schermo a comparsa che dovrebbe visualizzare un'animazione gif mentre il mio Swing gui si sta caricando e anche un po 'dopo.

Il mio schermo popup è un JDialog. LLa animazione dovrebbe essere visualizzato su un JLabel che è stato aggiunto alla JDialog seguente modo:

ImageIcon myImgIcon = getMyImgIcon(); 
JLabel imageLbl = new JLabel(myImgIcon); 
add(imageLbl, BorderLayout.CENTER); 

Ora la cosa è che l'animazione viene visualizzato solo dopo che la GUI è stato caricato. Credo che mentre la GUI si sta caricando (che è un'operazione pesante nella mia applicazione) l'EDT è così impegnato da non poter eseguire l'animazione.

Vedere How do I show a animated GIF image using a thread.

Ora il problema è che per me è sbagliato caricare la GUI su un thread diverso (non EDT), quindi non so come risolvere il problema.

Qualcuno ha un'idea?

+5

considerare l'utilizzo di un [schermata di avvio] (http://docs.oracle.com/javase/tutorial/uiswing/misc/splashscreen.html) invece se è per lo start-up della tua applicazione – Robin

risposta

6

Devi solo liberare il thread EDT di alcuni compiti pesanti e farli in un thread separato. In tal caso, l'animazione gif funzionerà insieme ad altri processi in esecuzione.

È inoltre possibile creare l'interfaccia dell'applicazione in un thread separato (sì sì, non all'interno dell'EDT) ma solo fino a quando non viene visualizzato. Successivamente è necessario apportare tutte le modifiche all'interno dell'EDT, altrimenti si potrebbero incontrare molti problemi.

È anche possibile caricare più elementi dell'interfaccia utente in un thread separato in seguito, ma assicurati di aggiungerli a frame/contenitori visualizzati all'interno di EDT: questa è la cosa più importante.

Ecco un piccolo esempio di interfaccia "heavy-like" di carico:

public static void main (String[] args) throws InvocationTargetException, InterruptedException 
{ 
    // Main window 

    final JFrame frame = new JFrame(); 

    final JPanel panel = new JPanel (new FlowLayout (FlowLayout.LEFT, 5, 5)) 
    { 
     public Dimension getPreferredSize() 
     { 
      Dimension ps = super.getPreferredSize(); 
      ps.width = 0; 
      return ps; 
     } 
    }; 
    frame.add (new JScrollPane (panel)); 

    frame.setSize (600, 500); 
    frame.setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE); 
    frame.setLocationRelativeTo (null); 

    SwingUtilities.invokeAndWait (new Runnable() 
    { 
     public void run() 
     { 
      frame.setVisible (true); 
     } 
    }); 

    // Load dialog 

    final JDialog load = new JDialog (frame); 

    JPanel panel2 = new JPanel (new BorderLayout()); 
    panel2.setBorder (BorderFactory.createEmptyBorder (15, 15, 15, 15)); 
    load.add (panel2); 

    final JProgressBar progressBar = new JProgressBar (0, 100); 
    panel2.add (progressBar); 

    load.setModal (false); 
    load.pack(); 
    load.setLocationRelativeTo (frame); 

    SwingUtilities.invokeAndWait (new Runnable() 
    { 
     public void run() 
     { 
      load.setVisible (true); 
     } 
    }); 

    // Heavy task (takes approx. 10 seconds + some time on buttons creation) 

    for (int i = 0; i < 100; i++) 
    { 
     Thread.sleep (100); 

     final JButton button = new JButton ("Button" + i); 
     final int finalI = i; 

     // Updating panel and progress in EDT 
     SwingUtilities.invokeLater (new Runnable() 
     { 
      public void run() 
      { 
       panel.add (button); 
       button.revalidate(); 
       progressBar.setValue (finalI); 
      } 
     }); 
    } 
} 

Come si può vedere - tutte le operazioni di aggiornamento dell'interfaccia sono realizzati in EDT, tutto il resto scorre dentro l'altro thread.

Si noti inoltre che il thread principale non è thread EDT, quindi possiamo fare qualcosa di pesante lì immediatamente.

In alcuni casi non è necessario visualizzare parti di interfaccia caricate subito, quindi è possibile aggiungerle tutte insieme alla fine dell'operazione "pesante". Ciò farà risparmiare tempo di caricamento e renderà il codice di inizializzazione molto più semplice.

Breve spiegazione su EDT e quello che ho detto nella risposta ...

...è stato qualcosa che ho trovato dopo aver lavorato per tre anni con Swing L & F e molte applicazioni basate su Swing. Ho scavato molte sorgenti Swing e ho trovato molte cose interessanti che non sono molto conosciute.

Come sapete - l'intera idea di singolo thread per gli aggiornamenti di interfaccia (la sua EDT in Swing) è di tenere ogni componente separato aggiornamenti visivi (ed i suoi eventi) in una coda ed eseguire uno per uno dentro quel filo . Ciò è necessario principalmente per evitare problemi di pittura poiché ogni componente all'interno di un singolo fotogramma viene dipinto sull'immagine singola che viene mantenuta in memoria. L'ordine di pittura è rigoroso, quindi un componente non sovrascriverà un altro sull'immagine finale. L'ordine di pittura dipende dall'albero dei componenti che viene creato aggiungendo alcuni componenti o contenitori all'interno di un altro contenitore (che è una cosa basilare che si fa quando si crea un'interfaccia dell'applicazione su Swing).

Per summ up - è necessario mantenere tutti i visivi aggiornamenti (metodi/operazioni che li potrebbero causare) all'interno del EDT. Qualsiasi altra cosa potrebbe essere eseguita al di fuori dell'EDT, ad esempio è possibile preparare l'interfaccia dell'applicazione all'esterno dell'EDT (anche in questo caso, a meno che non si aggiunga/rimuovi/sposti componente all'interno di un contenitore già visibile).

Ancora ci potrebbero essere alcuni problemi interni con quello in alcuni casi molto molto molto rari. C'è stata una buona discussione di tale questione un molto tempo fa qui:
http://www.velocityreviews.com/forums/t707173-why-does-jdk-1-6-recommend-creating-swing-components-on-the-edt.html

essere breve: dal 6 versione del JDK di Sun ha dichiarato nei documenti che anche Altalena creazione componenti dovrebbe essere fatto all'interno EDT per evitare possibili problemi. Potrebbero apparire in alcuni casi specifici con la creazione di interfacce pesanti a causa degli eventi che si verificano durante la creazione dei componenti.

In ogni caso, direi che in alcuni casi è possibile creare l'interfaccia esterna all'EDT per evitare che il caricatore/l'applicazione si blocchino. In altri casi, quando non importa se l'applicazione è bloccata per il tempo di creazione dell'interfaccia, è necessario utilizzare EDT. E non posso dire nulla di più specifico dal momento che tutto dipende dal tuo caso ...

+0

Hmm, grazie! Quello funzionerebbe. Ho una domanda per te però. Ogni documentazione ufficiale + tutorial trovati online esplicitamente e ripetutamente affermano che i componenti swing dovrebbero essere manipolati solo sull'EDT. Questa è la prima volta che vedo qualcuno che dice che finché sono invisibili non importa. Potresti spiegarmi perché si può infrangere la regola EDT se i componenti sono invisibili. Grazie! – whomaniac

+0

@ user1155122 ho aggiunto una descrizione nella risposta perché era troppo grande per un singolo commento –

+0

Google stava cercando online come Mad e tu sei l'unica fonte che mi ha dato alcune informazioni utili sull'argomento. Grazie!! – whomaniac

3

Forse stai provando a creare un'animazione che deve essere riprodotta solo all'inizio della tua applicazione, senza interferire con gli eventi o i componenti in arrivo. Quindi potresti provare a dare uno splash screen al . Leggi informazioni qui: http://docs.oracle.com/javase/tutorial/uiswing/misc/splashscreen.html

Nel link sopra, viene illustrato l'utilizzo di una classe denominata SplashScreen che è appena derivata dalla classe Frame. Quindi il meccanismo è così: si visualizza un frame separato (splash screen, le animazioni vanno qui) e dopo un po 'di tempo viene lanciata l'applicazione principale.

+0

Voglio davvero usare questa schermata iniziale! Lo faccio davvero! Tuttavia, c'è un piccolo problema. Voglio che la schermata iniziale venga eliminata quando lo voglio dopo che il metodo main() è terminato. Secondo i documenti, esce automaticamente quando main() esce! Come posso disabilitare questo. Qualche idea? – whomaniac

+0

@ user1155122: La schermata iniziale dovrebbe essere eliminata quando main() esiste. Tutto ciò che main() dovrebbe fare è avviare il thread di invio dell'evento Swing e quindi uscire. –

1

La classe 'ImageIcon' ti permette di caricare animazioni gif. Carico l'immagine con 'getResource()'. Per fare questo, di solito, noi usiamo la classe URL per passare il percorso del file. Il percorso non deve essere necessario in una macchina remota come può suggerire l'URL del nome.

URL url = this.getClass().getResource(path); 
Icon myImgIcon = new ImageIcon(url); 
JLabel imageLbl = new JLabel(myImgIcon); 
component.add(imageLbl, BorderLayout.CENTER); 

percorso sarà il percorso della gif all'interno della cartella della classe.

Riferimenti: http://docs.oracle.com/javase/tutorial/uiswing/components/icon.html#getresource

+0

correggi: this.getClass(). GetResource (percorso) –