2012-09-24 38 views
12

Hola Estoy escribiendo una aplicación GUI en Java 1.6 con Swing.Mostrar animación Gif en java

que tienen una pantalla emergente que debe mostrar una animación GIF durante mi swing interfaz gráfica de usuario se está cargando y también un poco después.

pantalla Mis pop-up es un JDialog. Lla animación se debe mostrar en un JLabel que se añadió a la JDialog la siguiente manera:

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

Ahora la cosa es que la animación sólo se muestra después de la interfaz gráfica de usuario se ha cargado. Creo que mientras se carga la GUI (que es una operación pesada en mi aplicación) el EDT está tan ocupado que no puede ejecutar la animación.

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

Ahora la cosa es que sería un error para mí para hacer la carga de interfaz gráfica de usuario en un hilo diferente (no EDT), así que no sé cómo resolver el problema.

¿Alguien tiene una idea?

+5

considerar el uso de un [pantalla de bienvenida] (http://docs.oracle.com/javase/tutorial/uiswing/misc/splashscreen.html) en lugar si se trata de la puesta en marcha de su aplicación – Robin

Respuesta

6

Sólo tienes que liberar hilo EDT de algunas tareas pesadas, de ponerlos en un hilo separado. En ese caso, la animación gif funcionará junto con otros procesos en ejecución.

También puede crear su interfaz de aplicación en un hilo separado (sí, no dentro del EDT) pero solo hasta que lo visualice. Después debe hacer todos los cambios dentro del EDT, de lo contrario, puede encontrar muchos problemas.

También puede cargar más elementos de IU en un hilo separado más adelante, solo asegúrese de agregarlos a los marcos/contenedores mostrados dentro de EDT, eso es lo más importante.

Aquí es un pequeño ejemplo de interfaz de carga "pesada similar":

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

Como se puede ver - todas las operaciones de actualización de la interfaz se hacen en EDT, todo lo demás se ejecuta dentro de otro hilo.

También tenga en cuenta que el hilo principal no es hilo EDT, por lo que podemos hacer algo pesado allí de inmediato.

En algunos casos no es necesario para mostrar las partes cargadas de la interfaz de inmediato, por lo que puede agregarlas al final de la operación "pesada". Eso ahorrará tiempo de carga y hará que el código de inicialización sea mucho más simple.

Breve explicación acerca de EDT y lo que dije en la respuesta ...

...fue algo que encontré después de trabajar tres años con Swing L & F y muchas aplicaciones basadas en Swing. Exploré muchas fuentes de Swing y encontré muchas cosas interesantes que no son ampliamente conocidas.

Como saben - la idea de un solo hilo de actualizaciones de la interfaz (la EDT en Swing) es acerca de mantener cada componente por separado cambios visuales (y sus eventos) en una cola y les lleve a cabo uno por uno dentro de ese hilo . Esto es necesario principalmente para evitar problemas de pintura ya que cada componente dentro de un solo cuadro está pintado en la única imagen de que se guarda en la memoria. El orden de la pintura es estricto, por lo que un componente no sobrescribirá otro en la imagen final. El orden de la pintura depende del árbol de componentes que se crea al agregar algunos componentes o contenedores dentro de otro contenedor (eso es algo básico que se hace al crear cualquier interfaz de aplicación en Swing).

Para resumir: debe conservar todas las visual actualizaciones (métodos/operaciones que podrían causarlas) dentro del EDT. Cualquier otra cosa podría hacerse fuera del EDT; por ejemplo, puede preparar la interfaz de la aplicación fuera del EDT (nuevamente, a menos que agregue/quite/mueva el componente dentro de un contenedor ya visible).

Todavía puede haber algunos problemas internos con eso en algunos casos muy, muy raros. Había una buena discusión de esa pregunta hace mucho aquí:
http://www.velocityreviews.com/forums/t707173-why-does-jdk-1-6-recommend-creating-swing-components-on-the-edt.html

Para ser breve: desde el 6 de versión del JDK de Sun afirma en documentos que incluso Columpio creación de componentes debe hacerse dentro de EDT para evitar posibles problemas. Pueden aparecer en algunos casos específicos con creación de interfaces pesadas debido a los eventos que ocurren mientras se crean los componentes.

De todos modos, diría que en algunos casos puede crear su interfaz fuera del EDT para evitar que el cargador/aplicación se bloquee. En otros casos, cuando no importa si la aplicación está bloqueada durante el tiempo de creación de la interfaz, debe usar EDT. Y no puedo decir nada más específico ya que todo depende de su caso ...

+0

Hmm, gracias! Eso funcionaria. Sin embargo, tengo una pregunta para ti. Cada documentación oficial + tutoriales que se encuentran en línea dicen explícita y repetidamente que los componentes oscilantes solo deben manipularse en el EDT. Esta es la primera vez que veo a alguien decir que, mientras sean invisibles, no importa. ¿Podría explicarme por qué se puede romper la regla EDT si los componentes son invisibles? ¡Gracias! – whomaniac

+0

@ user1155122 He agregado una descripción en la respuesta, ya que era demasiado grande para un solo comentario –

+0

Estaba buscando en línea en línea como Mad y usted es la única fuente que me dio información realmente útil sobre el tema. ¡¡Gracias!! – whomaniac

3

Tal vez usted está tratando de hacer una animación que se va a jugar justo en el inicio de su aplicación, sin interferir en los próximos eventos o componentes. Por lo tanto, es posible que desee probar pantallas de presentación. Lea sobre esto desde aquí: http://docs.oracle.com/javase/tutorial/uiswing/misc/splashscreen.html

En el enlace de arriba, demuestra el uso de una clase llamada SplashScreen que se deriva de la clase Frame. Así que el mecanismo es así: se muestra un marco separado (pantalla de bienvenida, aquí van sus animaciones) y después de un tiempo se inicia su aplicación principal.

+0

¡Realmente quiero usar esta pantalla de bienvenida! ¡Realmente lo hago! Sin embargo, hay un pequeño problema. Quiero que la pantalla de presentación se elimine cuando la desee después de que el método main() finalice. De acuerdo con los documentos, sale automáticamente cuando sale main()! ¿Cómo puedo desactivar esto? ¿Algunas ideas? – whomaniac

+0

@ user1155122: Se supone que la pantalla de bienvenida se desechará cuando main() exista. Se supone que todo lo principal() es iniciar el hilo de envío del evento Swing y luego salir. –

1

La clase 'ImageIcon' le permite cargar animaciones gif. Cargué la imagen con 'getResource()'. Para hacer esto, normalmente utilizo la clase URL para pasar la ruta del archivo. La ruta no necesita ser necesaria en una máquina remota como el nombre URL puede sugerir.

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

ruta de acceso será la ruta de acceso del archivo .gif dentro de la carpeta de la clase.

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

+0

por favor, corrija: this.getClass(). GetResource (ruta) –