Dúvidas - JPanel e Graphics

4 respostas
pagrippa

To aprendendo a fazer uma tela com um desenho de duas linhas usando as classes JPanel e Graphics, e surgiram duas duvidas:

1º Como é possível usar métodos da classe Graphics, por exemplo o método drawLine, se no código abaixo não esta sendo criado um objeto Graphics? Pelo menos não da maneira que eu aprendi até agora, que é usando new.

2º Não entendi a instrução super. Para mim, parece que ela faz com que, na linha super.paintComponent( g ), quando esse mesmo método da classe DrawLine for chamado, o método da classe JPanel fará com que seja executado de maneira correta, por eu estar ao mesmo tempo criando instruções nesse método. Ta muito errado isso?

Abaixo segue o código:

import java.awt.Graphics;
import javax.swing.JPanel;

public class DrawPanel extends JPanel
{

   public void paintComponent( Graphics g )
   {

      super.paintComponent( g );

      int widht = getWidht(); //largura total
      int height = getHeight(); //altura total

      g.drawLine( 0, 0, widht, height ); //desenha uma linha a partir do canto superior esquerdo até o inferior direito

      g.drawLine( 0, height, widht, 0 ); //desenha uma linha a partir do canto inferior esquerdo ate o superior direito

   } //fim do método paintComponent

} //fim da classe DrawPanel

Se puderem tirar essa dúvida pra mim, ficarei muito grato.

Ja pesquisei no Google explicações detalhadas para essas duvidas, e não encontrei nada que sanasse as mesmas.

4 Respostas

davidbuzatto
pagrippa:
1º Como é possível usar métodos da classe Graphics, por exemplo o método drawLine, se no código abaixo não esta sendo criado um objeto Graphics? Pelo menos não da maneira que eu aprendi até agora, que é usando new.
O new é usado em algum momento, mas não é e nem deve ser responsabilidade sua. Quem vai criar um objeto Graphics, que por sua vez vai estar "amarrado" ao SO é a JVM, não você. Na verdade nem é um Graphics que é criado, mas sim um Graphics2D (que estende Graphics), que por sua vez, varia de implementação para implementação da JVM, afinal, no Windows o "motor gráfico" é acessado de uma forma, enquanto no Linux é de outra. Esse objeto que é criado nos bastidores é passado para o método paintComponent (herdado de JComponent), que por sua vez é implementado em um JPanel com o objetivo de desenhar um painel. Aqui já estamos entrando na segunda dúvida...
pagrippa:
2º Não entendi a instrução super. Para mim, parece que ela faz com que, na linha super.paintComponent( g ), quando esse mesmo método da classe DrawLine for chamado, o método da classe JPanel fará com que seja executado de maneira correta, por eu estar ao mesmo tempo criando instruções nesse método. Ta muito errado isso?
Como o método paintComponent é usado para desenhar o componente, quando você estende um JPanel que você vai desenhar alguma coisa, vc precisa fornecer uma implementação nova para o método paintComponent, sobrescrevendo-o. Legal, mas e o super? A chamada super.paintComponent quer dizer que antes de mais nada, deve-se chamar o método paintComponent da superclasse, permitindo que haja uma chance do desenho padrão de um JPanel ser feito. Ou seja, primeiro vc garante que o painel padrão seja desenhado e então você desenha o que quiser nele. Apesar de você poder usar diretamente o objeto Graphics (na verdade é um Graphics2D) que é passado para o método paintComponent, é recomendado que você faça uma cópia do contexto gráfico original para que caso você precise executar alguma transformação no contexto (por exemplo, fazer uma translação nas coordenadas ou rotacionar o contexto) para que essa transformação não seja propagada a outros componentes que podem talvez usarem o mesmo objeto Graphics para se desenharem.

Como eu disse, se não forem feitas transformações, não precisa criar uma cópia do contexto, mas vai que um dia vc decide usar o método translate para mudar a origem do sistema de coordenadas. Ai você pode ter problemas caso algum outro componente use o mesmo objeto Graphics para se desenhar, pois o que antes era (0;0) (canto superior esquerdo), agora pode ser (-20;-20) caso tenha feito uma translação para (20;20). Eu normalmente só crio um novo contexto se eu tiver certeza que vou fazer alguma transformação, mas por via das dúvidas, o recomendado é sempre criar uma cópia do contexto. Atualizei seu código para refletir isso.

import java.awt.Graphics;
import javax.swing.JPanel;

public class DrawPanel extends JPanel {

   // desenhando ESTE painel
   public void paintComponent( Graphics g ) {

      // primeiro garante o desenho padrão do painel
      super.paintComponent( g );

      // cria uma cópia do contexto gráfico
      Graphics newG = g.create();

      // hora de desenhar :D (note o uso da cópia do contexto)

      int widht = getWidht(); //largura total
      int height = getHeight(); //altura total

      // usa a cópia do contexto para desenhar
      newG.drawLine( 0, 0, widht, height ); //desenha uma linha a partir do canto superior esquerdo até o inferior direito
      newG.drawLine( 0, height, widht, 0 ); //desenha uma linha a partir do canto inferior esquerdo ate o superior direito

      // libera a cópia do contexto gráfico
      newG.dispose();

   } //fim do método paintComponent

} //fim da classe DrawPanel
E

Bem, eu não sou especialista, mas vou tentar dar uma explicação razoável. Se eu estiver errado, por favor me corrijam.

É verdade, nenhum objeto foi criado nesse seu código, mas não quer dizer que ele não foi criado em outro lugar. (???)

O ponto chave para enteder essa e a sua outra dúvida está na linha 04:

(...)
public class DrawPanel extends JPanel
(...)

Não sei se tá mostrando, mas eu coloquei o “extends JPanel” em evidência. Uma das vantagens, que no seu caso se tornou uma complicação, de se trabalhar com herança, é que seu código não precisa ser totalmente reescrito, só as partes que você quer mudar. No caso, você está reescrevendo o método paintComponent( Graphics g ). Ou seja, tem um método da classe JPanel que, toda vez que vai redenrizar o JPanel na tela, chama o paintComponent e passa o objeto Graphics que vai ser usado para o desenho.

Resumindo: o objeto é criado em um método de JPanel e passado pelo parâmetro Graphics g para que você possa usar para fazer os desenho que você quiser.

É por aí. Essa linha só garante que o Painel tenha as caracteríticas certas, como cor de fundo, por exemplo. É como se você executasse o método do pai pra deixar tudo funcionando bem, pra só então fazer o que você quer. Ela garante que as funcionalidades de JPanel não se percam por você estar reescrevendo esse método na classe filha.

Qualquer dúvida, posta aí.

Francisco_Silva

É uma excelente explicação sobre de onde vem o objeto Graphics2D…
Veja a documentação para melhor entender sobre o assunto…
http://download.oracle.com/javase/1.4.2/docs/api/java/awt/Graphics.html

http://download.oracle.com/javase/1.4.2/docs/api/java/awt/Graphics2D.html

Eu que até um certo tempo usava windows e linux ao mesmo tempo(agora só linux), pude verificar que alguns recursos da API java funciona de forma diferentes para cada sistema operacional… principalmente quando se trata de Frame, System e Graphics. Perceba que utilizam de recursos do sistema operacional e por conseqüência podem ser implementados de forma diferentes em cada VM.
Por isso, para se implementar um sistema realmente multi-plataforma é preciso de testes constantes em cima de cada plataforma que se deseja rodar o sistema.

Desculpe por ter desviado do assunto em questão. mas realmente gostei da iniciativa da pergunta, ja que muitos acabam por não perceber esses detalhes.

pagrippa

Valew, gurizada…

Com as tres resposras, ja consegui entender o que eu queria…

Valew mesmo… agora posso voltar a aprender mais coisas “em paz com a minha curiosidade”, rsrs

Criado 24 de janeiro de 2011
Ultima resposta 25 de jan. de 2011
Respostas 4
Participantes 4