rombo hilos hechos dibujos dibujar contador con codigo clase java multithreading components jframe paint

hechos - hilos en jframe java



Los componentes instanciados desde dentro de un hilo no se repintan en un JFrame en Java (3)

Tengo una sola clase como esta

public class BlockSpawner implements Runnable{ public static long timeToSpawn; private GtrisJFrame frame; public BlockSpawner(GtrisJFrame frame) { this.frame = frame; timeToSpawn = 2000; } public void run() { while(true) { try { Thread.sleep(timeToSpawn); } catch(InterruptedException e) { //Unhandled exception } //After awake, instanciate 2 blocks //get the position of the first one int index = Block.getRandomStartPosition(); new Block(frame, index); new Block(frame, index+1); } }

}

Instalo esta clase en la clase principal de JFrame, y comienzo su hilo así:

private void initBlockSpawner() { spawner = new BlockSpawner(this); new Thread(spawner).start(); }

Llamo a esta función initBlockSpawner () dentro del JFrame Constructor. Block Class es realmente un poco grande, pero en pocas palabras, implementa ejecutable, y llama a su método run () al final de su constructor. El método run () solo hace que un bloque caiga a cierta velocidad. He intentado crear instancias nuevas de bloques en el constructor de JFrame y funcionan, se vuelven a pintar y caen. Pero cada vez que quiero crear instancias de bloques de otros hilos, parecen caer (es decir, sus propiedades actualizan cada ciclo), pero no se pintan en el JFrame.

Como información adicional, estoy usando NetBeans, y dado que el punto de entrada de la aplicación está en la clase JFrame, el método principal es el siguiente:

public static void main(String args[]) { java.awt.EventQueue.invokeLater(new Runnable() { public void run() { new GtrisJFrame().setVisible(true); } }); }

No tengo mucha experiencia con Java Threads, awt events y swing components. Pero algo que leí aquí me hace pensar que mi problema es que solo un hilo tiene el control sobre los componentes del swing, o algo ... ¿Hay alguna forma de resolver mi problema?

Gracias por adelantado.

EDITAR: Información adicional, cada vez que compruebo el método aString en los cubos de subprocesos creados a partir de subprocesos, me dan esto [, 0,0,0x0], pero cuando los instancia en la misma clase JFrame me dan este resultado [, 0, 0,328x552] y aparecen en el marco. este valor de 328x552 es lo mismo que la Dimensión del componente devuelta por getPreferredSize () ... Intenté forzarlos a esa dimensión instanciarlos así:

new Block(this, index).setPreferredSize(new Dimension(328, 552));

Pero no funcionó, ¿alguien sabe qué podría significar este valor [, 0,0,328x552]?

¡Gracias a todos, creo que ya casi llegamos!

EDIT 2: Me di cuenta de que el tamaño del componente es x: 0 y: 0, ¿por qué es esto? Cambio el método run () de BlockSpawner a algo como esto:

public void run() { while(true) { System.out.println("SPAWN"); int index = Block.getRandomStartPosition(); new Thread(new Block(frame, index)).start(); new Thread(new Block(frame, index+1)).start(); try { Thread.sleep(timeToSpawn); } catch(InterruptedException e) { //Unhandled exception } } }

¡En la primera carrera, todo va bien! incluso el par de bloques pintan en el JFrame y se caen correctamente, pero después del Thread.sleep (), el resto de ellos acaba de ser instanciado, pero su método getSize () me da x: 0 y: 0; ¿Sigue relacionado de algún modo con el problema del hilo de distribución One Dispatcher? o es algo diferente ahora?


A mí me parece (aunque no puedo decir por su código anterior) que está tratando de agregar componentes a un JFrame en vivo (es decir, uno que se ha mostrado en pantalla o ''realizado'') de un hilo que no sea el envío del evento hilo. Eso es una violación del modelo de enhebrado Swing, y le causará infinitos problemas.

Si desea realizar cambios en un objeto Swing de un hilo diferente, empaquete el cambio en Runnable y envíelo al hilo de envío utilizando EventQueue.invokeLater () o invokeAndWait ().

EDITAR: más información

Algunos comentarios adicionales (no relacionados directamente con su problema, pero importantes de todos modos): realizar una actividad en un constructor probablemente no sea una buena idea. Subclasificar JFrame para agregarle componentes probablemente tampoco sea una buena idea. De hecho, hacer estas operaciones en un JFrame en lugar de un JPanel probablemente tampoco sea el mejor enfoque.

Tomando estos a su vez:

  1. Los constructores deben usarse para realizar la configuración inicial en objetos, no para invocar el comportamiento. Esta separación ayuda a mantener su diseño limpio y sostenible. Aunque podría parecer más fácil hacerlo de esta manera, recomiendo que no lo haga. En algún momento de su diseño, puede decidir que es más eficiente crear estos objetos por adelantado y solo usarlos más adelante.

  2. Subclasificar un JFrame para agregar componentes generalmente no es una buena idea. ¿Por qué? ¿Qué sucede si decides que quieres utilizar un JFrame especializado que tenga algún otro comportamiento que desees? ¿Qué sucede si decide utilizar un marco de aplicación que le proporcione el JFrame (esto es típico en los casos en que el marco puede querer rastrear eventos de cierre de ventana para que pueda guardar el tamaño y la ubicación de la ventana). De todos modos, un montón de razones. Empaquete su comportamiento en una clase no relacionada con GUI, y úselo para inyectar comportamiento en el JFrame (o JPanel).

  3. Considere usar un JPanel en lugar de un JFrame. Siempre puede agregar el JPanel a un JFrame si lo desea. SI habla directamente con JFrame, ¿qué sucede cuando decide que quiere tener dos de estos paneles uno al lado del otro en un único contenedor?

Entonces, sugiero que hagas algo más como:

BlockAnimator animator = new BlockAnimator(); DispatchThread.invokeLater( new Runnable(){ public void run(){ JPanel blockAnimationPanel = new JPanel(); Block block = new Block(...); blockAnimationPanel.add(block); JFrame mainFrame = new JFrame(); mainFrame.add(blockAnimationPanel); animator.start(); // note that we probably should start the thread *after* the panel is realized - but we don''t really have to. } } public class BlockAnimator extends Thread{ private final List<Block> blocks = new CopyOnWriteArray<Block>(); // either this, or synchronize adds to the list public void addBlock(Block block){ blocks.add(block); } public void run(){ while(true){ // either put in a cancel check boolean, or mark the thread as daemon! DispatchThread.invokeAndWait( new Runnable(){ public void run(){ for(Block block: blocks){ block.moveTo(....); // do whatever you have to do to move the block } } } ); // I may have missed the brace/paren count on this, but you get the idea spawnNewBlockObjects(); Thread.sleep(50); } } }

El código anterior no se ha verificado para la precisión, etc.

En teoría, podría tener un hilo separado para generar los nuevos bloques, pero lo anterior es bastante sencillo. Si decide implementar con un único hilo de fondo como he mostrado anteriormente, puede usar una lista de matrices simple para la lista de bloques porque no habrá condiciones de carrera en ese objeto.

Un par de otras ideas sobre esto:

  1. En lo anterior, el animador de bloque se puede gestionar independientemente de los bloques. Podría, por ejemplo, agregar un método de pausa () que pausaría todos los bloques.

  2. Tengo la actualización de animación para todos los bloques que ocurren en la misma llamada de envío de hilo. Dependiendo del costo de la animación, puede ser mejor calcular las nuevas coordenadas en el hilo de fondo, y solo publicar la actualización de posición real en el EDT. Y puede optar por emitir un invokeAndWait por separado (o posiblemente usar invokeLater) para cada actualización de bloque. Realmente depende del carácter de lo que estás haciendo.

Si el cálculo de dónde mover el bloque es retorcido, considere separar el cálculo del movimiento real. Entonces tendrías una llamada que obtendría el nuevo Punto para el objeto, y luego otra llamada que realmente haría el movimiento.


Swing no es compatible con el subprocesamiento múltiple, por lo que siempre que necesite interactuar con él, debe hacerlo desde el subproceso de evento AWT.

Esto es lo que está sucediendo en el método main () agregado por netbeans. java.awt.EventQueue.invokeLater programa un ejecutable para ser ejecutado en la cola de eventos AWT.

Normalmente, usted podría hacer lo mismo con su BlockSpawner Runnable pero debido a que necesita un retraso, Sleep () bloquearía la cola de eventos y causaría problemas / retrasos con la entrada del usuario.

Para solucionar este problema, le sugiero que utilice un SwingWorker que permita que las tareas se ejecuten en segundo plano y luego se vuelvan a sincronizar con la cola de eventos cuando hayan finalizado.

En su caso, debe realizar el comando sleep () en el método doInBackground () y luego crear sus nuevos componentes en el método done ().


Una alternativa a mi otra respuesta es usar un temporizador javax.swing.Timer

Esto proporciona la capacidad de programar una acción para que ocurra en el hilo de envío del evento a una velocidad especificada y no requiere Java 6

Puede programar su BlockSpawner con el siguiente código:

int timeToSpawn = 2000; ActionListener blockSpawner = new ActionListener() { public void actionPerformed(ActionEvent evt) { int index = Block.getRandomStartPosition(); new Block(frame, index); new Block(frame, index+1); } }; new Timer(timeToSpawn, blockSpawner).start();

Esta es probablemente la solución más simple, ya que no requiere hilos adicionales. Solo asegúrate de usar la clase Timer en javax.swing y no en java.util; de lo contrario, es posible que no estés ejecutando en el hilo de envío del evento.