Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Problema con Java y Graphics

Iniciado por jalbam, 12 de Enero de 2008, 01:00:58 PM

« anterior - próximo »

jalbam

Hola, buenas.

He creido conveniente poner esta duda aquí y no en el foro de Programación gráfica ya que, aunque trate de un método para dibujar, no es una duda sobre programación gráfica si no más bien del lenguaje Java y sus objetos referentes a los gráficos.

Mi problema es el siguiente:

Tengo un objeto de formulario (que deriva de javax.swing.JFrame) el cual tiene un par de contenedores. Puedo dibujar en ellos dentro del método paint de este mismo objeto de formulario, pero no hay forma de hacerlo desde otro metodo del mismo objeto o desde otro objeto. Además, el método paint sólo se ejecuta una vez y a mi me gustaría llamarlo cuando yo quisiera.

Os explico un poco como lo tengo hecho para que se me entienda mejor:

* Tengo un objeto formulario llamado TetrisForm (que deriva de javax.swing.JFrame) el cual contiene un par de contenedores, entre ellos uno llamado PanelJuego. No utilizo Applets, aunque me imagino que debe ser muy similar.

* Puedo pintar en PanelJuego sin problemas desde un método llamado paint (public void paint(Graphics g))  en el mismo objeto TetrisForm. Por ejemplo, con este codigo:
g.setColor(java.awt.Color.GREEN);
g.fillRect(1, 1, 200, 200);


* Tengo otro objeto con un método con la siguiente estructura: representarTodo(Graphics g). Dentro pongo el mismo código de dibujo de más arriba (el del método paint).

* Problema: pasando por parámetro al método representarTodo() el contenedor correspondiente (con PanelJuego.getGraphics()), no pinta nada. He intentado llamar a update(), a repaint()... pero nada. Tampoco creando un método llamado paint() dentro del objeto que tiene representarTodo().

* Yo quiero que me pinte desde el método representarTodo() y en el momento que yo quiera, no sólo al iniciar el programa. Cabe recordar que el método representarTodo() está en otro objeto que no es TetrisForm, aunque para pasarle el objeto Graphics no tengo problema. Lo digo por si acaso.

Estoy utilizando NetBeans IDE 5.5.1 bajo Windows XP, con swing.

Espero haber sido suficiente claro y explícito. La verdad es que parece un poco liante pero en realidad es una cuestión bastante sencilla y seguro que tiene facil solución. El problema es que por más que he buscado no he podido dar con ella.

A ver si alguien puede echarme una mano, por favor.

Muchas gracias por todo de antemano :)
-----
Juan Alba Maldonado

Warchief

Para mi editor de mapas uso java y el código es similar a:


public class VistaMapa extends javax.swing.JPanel {
// ...

   // Aplica una herramienta al hacer click con el ratón
   private void Aplicar(java.awt.event.MouseEvent evt)
   {
       // Hace cosas ...
       
       this.repaint(); // Con esto llamará a paintComponent después
   }

   public void paintComponent(java.awt.Graphics g)
   {
       super.paintComponent(g);
                     
       // Pintar lo que tiene el mapa
       java.awt.Graphics2D g2d = (java.awt.Graphics2D)g;
       // ... etc        
   }

}
   
       
Prueba si el paintComponent te funciona.

jalbam

Gracias por la respuesta, pero mi clase deriva de JFrame y no de JPanel. Por lo tanto, no existe el metodo paintComponents() aunque sí existe paintComponents() (acabado en "s"). Lo he probado con éste y no funciona :(

EDITADO: Los dos componentes que decía arriba sí son objetos del tipo JPanel, pero no tengo ninguna clase que derive de JPanel. Así están declarados:

       PanelJuego = new javax.swing.JPanel();
       PanelFichaSiguiente = new javax.swing.JPanel();
-----
Juan Alba Maldonado

Zaelsius

Desde la ventana padre (JFrame), puedes forzar el repintado de los componentes hijos que te interesen (p.ej. un Canvas o JPanel) invocando el método repaint() de éstos. Olvídate de pasar el contexto de dibujado (objeto Graphics), no hace falta.

En pocas palabras:

Padre::representarTodo()
{
- para cada componente hijo que quieras redibujar(normalmente solo uno, el canvas principal)
--- hijo.repaint()
}


Si has sobrecargado adecuadamente el método paint() del componente hijo (jframe, canvas, lo que sea), el pseudocódigo de arriba debería funcionar sin problemas.

shephiroth

A ver, si vas a hacer un juego en java, tienes dos soluciones:

1) Separar la logica del dibujado, y llamar al metodo repaint del JFrame donde quieras. Si no has redefinido el metodo paint del frame, por defecto llama al repaint de todos sus componentes, por lo q con tener redefinidas las funciones de tus dos componentes es suficiente. Si has redefinido el metodo paint del frame, necesitaras invocar al paint del frame (es decir, super.paint(g);).
Ahora q me doy cuenta, lo doy por supuesto, pero, me imagino que habras añadido tus componentes al jframe, no??? en caso contrario, necesitas añadirlos al panel, invocando su metodo add(Component c)

2) Olvidarte del repaint de AWT, y crear tu gameLoop antaño de c....Para esto hay q hacer varias cosas:

2.1) Indicarle al JFrame que ignore las llamadas de AWT de repitarse, para esto invocamos al metodo setIgnoraRepaint(true);

2.2) En el main de tu programa es muy posible que creases un new myJFrameWindow(...) y sin mas terminar....total java hasta q no cierras la ventana no terminara el programa. Pero en este caso, tienes q crear tu gameLoop, tendrías que hacer algo como:

class myJFrameWindow extends JFrame implements.... {
...
public static void main() {
...
//configuraciones previas
...
myJFrameWindow v = new myJFrameWindow(...);
while (true) {
v.logic();
v.draw();
try {Thread.sleep(20);} catch (InterruptedException ie) {}
}
}
}


P.D: Como ves en el segundo punto, no utilizo la funcion paint, sino otra draw. Para el remake de zelda que estoy haciendo he investigado, y en todas las paginas que he visto que hablaban sobre el tema aconsejaban crearse una funcion aparte del paint, y nisiquiera redefinir dicha funcion.

Mars Attacks


jalbam

Muchas gracias a todos por las respuestas, de verdad :)

La verdad es que tenéis razón y yo no entendía por qué algo tan sencillo en realidad se me complicaba tanto. No podía ser que para hacer algo que debería ser facil de realizar, y de varias maneras posibles, no me salía de ninguna forma.

Antes de leer vuestros comentarios ya lo había probado todo o casi todo lo que comentáis. Pero nada resultaba.

Al final ayer descubrí donde estaba el fallo, tonto de mi :)

1) Tengo un objeto, que deriva de JFrame, llamado TetrisForm.

2) Un objeto Juego que interactúa con todo lo demás respectivo al juego (objetos Pieza, objeto Tablero, objeto Panel...) y contiene el ciclo del juego, entre otras cosas, y parte de la lógica del juego. Ayuda a abstraer, para que sólo sea necesario manipular éste objeto para controlar el juego en sí.

3) Un objeto Main que tiene el main() principal, con varios métodos/funciones (son estáticas) y propiedades (estáticas tambien). Éste es el que debe comunicar al objeto Juego con el objeto TetrisForm (bueno, en concreto con un JPanel que contiene el TetrisForm). Maneja el objeto Juego y el objeto TetrisForm. Es el objeto principal del programa.

4) En el objeto Main se hacía una instancia del objeto TetrisForm y luego ejecutaba el main que tambien tenía TetrisForm. Más o menos así:

Main.ventanaPrincipal = new TetrisForm();
Main.mostrarVentana(); //Esto ejecuta Main.ventanaPrincipal.main(null);.


5) Luego le pasaba el JPanel (anteriormente lo hacía con el objeto Graphics, pero tampoco iba), que estaba dentro del objeto TetrisForm, al objeto juego:

juego = new Juego(Main.ventanaPrincipal.getPanelJuego()); //getPanelJuego() devuelve el JPanel llamado PanelJuego contenido en TetrisForm

6) No se mostraba nada, pero si actualizaba el JPanel que recibía el objeto Juego dentro del método paintComponents() de la objeto TetrisForm, entonces sí que iba. De esta forma más o menos:

if (Main.juego.getJuegoComenzado())
{
       Main.cambiarContenedorGraficoJuego(this.getPanelJuego()); //Vuelve a actualizar el objeto JPanel que se había enviado al objeto Juego desde su constructor.
}


7) El problema es el siguiente: la instancia del JPanel que se enviaba al constructor del objeto Juego no era la misma que contenía la ventana que se veía en pantalla. Por eso, al ejecutar el método paintComponents() de la ventana que se veía, si esta volvía a enviar el JPanel correcto entonces sí funcionaba. La culpa la tenía el código del método main() del objeto TetrisForm (la ventana):

java.awt.EventQueue.invokeLater
(
       new Runnable()
       {
               public void run()
               {
                       new TetrisForm().setVisible(true);
               }
       }
);


8 ) Al final lo he solucionado cambiado la linea con el new -que es la que creaba otro objeto distinto que el que yo pasaba al constructor del objeto Juego- por ésta:

Main.ventanaPrincipal.setVisible(true);


En fin, siento haber escrito un post para algo tan tonto, pero de verdad que me costó bastantes horas encontrar el fallo. A lo mejor a alguien que lea esto algún día le ayude a solucionar el mismo problema, aunque dudo que cometa el mismo error que yo :)

Como el formulario lo cree con la interfaz visual que ofrece el NetBeans, generaba un código (mucho) que yo no conozco y no controlo. Y eso a veces es peligroso.

Muchas gracias a todos otra vez.

¡Hasta pronto! :)
-----
Juan Alba Maldonado






Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.