Le Basi del disegno 2D in java

 

Ci sto lavorando

 

Obiettivo: disegnare una semplice ellisse all'interno di una finestra (JFrame).

 

 

Varianti:

  1. in un unico file.java tutto il codice, con il JFrame istanziato all'interno della casse principale
  2. in un unico file.java tutto il codice, con la classe principale che estende JFrame
  3. in due sorgenti: il principale rappresenta il JFrame e sarà l'eseguibile, l'altro il JPanel (ho JComponent) che andrà a sovrascrivere il metodo paintComponent che mi permetterà di disegnare
  4. in due sorgenti: il principale rappresenta l'eseguibile con il Jpanel, l'altro il JFrame
  5. in tre sorgenti: il principale che sarà la classe eseguibile, un JFrame che costruirà la finestra e un JPanel che disegnerà l'ellisse

Variante n. 1

 3 import java.awt.*;
 4 import java.awt.geom.*;
 5 import javax.swing.*;
 6  
 7 public class Graf_01{  
 8     public static void main(String[] args) {
 9        SwingUtilities.invokeLater(new Runnable() {
10            public void run() {
11              JFrame f = new JFrame("Disegno un ellisse");
12                 f.setBounds(800, 500, 300, 250);
13                 f.getContentPane().setBackground(Color.WHITE);
14                 f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
15                 f.setLayout(null);
16                 f.add(new MyComp());
17                 f.setVisible(true);          
18             }            
19        });
20     }
21 }
22 class MyComp extends JComponent{
23     public MyComp(){
24         setBounds(0, 0, 300, 250);
25     }
26     final BasicStroke spessore = new BasicStroke(5.0f);
27     public void paintComponent(Graphics g) { 
28        super.paintComponent(g);
29        Graphics2D g2d =(Graphics2D)g;
30        g2d.setBackground(Color.yellow);
31        g2d.setStroke(spessore);
32        Ellipse2D.Double fc = new Ellipse2D.Double(95,30,100,150);
33        g2d.draw(fc);
34     }
35 }
          

 

Discussione:

Ci sono due classi la prima Graf_01 ha il metodo main è quindi eseguibile. Qui abbiamo una particolarità una classe anonima che implementa una interfaccia Runnable il cui metodo da sovrascrivere (@Override) è il metodo run che verrà eseguito automaticamente. Poi abbiamo SwingUtilities.invokeLater(new Runnable() { questo permetterà di eseguire il tutto all'interno del thread EDT. Le interfacce GUI andrebbero sempre eseguite nell'EDT.

(seguire i link per gli approfondimenti)

 

Altra particolarità da mettere in evidenza è il fatto che nel contenitore padre il JFrame abbiamo impostato un layout manager nullo (riga 14) ma il new MyComp() ha per default una dimensione di 0x0. Quando invece c'era il BorderLayout predefinito del contentPane, il componente andava ad occupare implicitamente tutta la parte CENTER del contentPane. Per questo per poter vedere l'ellisse dovremo dare una dimensione al MyComp ragionevole, qui ho dato la stessa dimensione del JFrame (riga 23).

Avrei evitato questo semplicemente senza annullare il layout manager così:

 

 3 import java.awt.*;
 4 import java.awt.geom.*;
 5 import javax.swing.*;
 6  
 7 public class Graf_01{  
 8     public static void main(String[] args) {
 9        SwingUtilities.invokeLater(new Runnable() {
10            public void run() {
11              JFrame f = new JFrame("Disegno un ellisse");
12                 f.setBounds(800, 500, 300, 245);
13                 f.getContentPane().setBackground(Color.WHITE);
14                 f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);                 
15                 f.add(new MyComp());
16                 f.setVisible(true);          
17             }            
18        });
19     }
20 }
21 class MyComp extends JComponent{  
22     final BasicStroke spessore = new BasicStroke(5.0f);
23     public void paintComponent(Graphics g) { 
24        super.paintComponent(g);
25        Graphics2D g2d =(Graphics2D)g;      
26        g2d.setStroke(spessore);
27        Ellipse2D.Double fc = new Ellipse2D.Double(95,30,100,150);
28        g2d.draw(fc);
29     }
30 }

La classe MyComp qui estende la classe JComponent ma poteva benissimo estendere la classe JPanel non sarebbe cambiato nulla. Qui abbiamo le nozioni basilari per poter disegnare all'interno di un qualsiasi componente, in questo caso un JComponent o un JPanel ma poteva benissimo essere una jlabel od altro. In pratica si tratta di riscrivere il metodo painComponent che riceve automaticamente dal sistema un oggetto di tipo Graphics l'oggetto g, attenzione è un oggetto Graphics quindi appartenente alla libreria java.awt.graphics la vecchia libreria grafica piuttosto limitata rispetto alla più avanzata java.awt.graphics2D allora per far diventare il nostro oggetto g un g2d bisogna eseguire un casting (riga 24) non dopo aver richiamato il costruttore della classe madre con super (riga 23). Per il resto non ci rimarrà che disegnare con tutti i metodi messi a disposizione dall'oggetto g2d.

 

La classe MyComp non ha specificatore di accesso e così funziona, se si prova ad attribuirgli qualsiasi altro specificatore da errore (Sugli specificatori di accesso vedi qui). In realtà lo specificatore che viene impostato di default è package-level ma non puoi usarlo scrivendolo. Il livello di accesso package-level (o detto package-private) NON ha un corrispondente modificatore di accesso. Lo ottieni solo come "default" se non metti esplicitamente public/protected/private.

 

Variante n. 2

 3 import java.awt.*;
 4 import java.awt.geom.*;
 5 import javax.swing.*;
 6 
 7 public class Graf_02 extends JFrame {
 8 
 9     public Graf_02() {
10         initComponents();
11     }
12    
13     @SuppressWarnings("unchecked")
14     // <editor-fold defaultstate="collapsed" desc="Generated Code">                          
15     private void initComponents() {
16 
17         setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
18         setTitle("Disegna un ellisse (Graf_02)");
19         setBackground(new java.awt.Color(255, 255, 0));
20         setBounds(new java.awt.Rectangle(800, 500, 300, 240));
21 
22         pack();
23     }// </editor-fold>                        
24 
25     public static void main(String args[]) {
26         /* Set the Nimbus look and feel */
...
48         //</editor-fold>
49 
50         /* Create and display the form */
51         java.awt.EventQueue.invokeLater(new Runnable() {
52             public void run() {
53                 Graf_02 f = new Graf_02();
54                 f.setBounds(800, 500, 300, 250);
55                 f.getContentPane().setBackground(Color.WHITE);
56                 f.add(new MyComp());
57                 f.setVisible(true);
58             }
59         });
60     }
61 
62     // Variables declaration - do not modify                     
63     // End of variables declaration                   
64 }
65 class MyComp extends JComponent{    
66     final BasicStroke spessore = new BasicStroke(5.0f);
67     public void paintComponent(Graphics g) { 
68        super.paintComponent(g);
69        Graphics2D g2d =(Graphics2D)g;
70        g2d.setStroke(spessore);
71        Ellipse2D.Double fc = new Ellipse2D.Double(95,30,100,150);
72        g2d.draw(fc);
73     }
74 }    

 

Discussione:

Allora qui abbiamo generato automaticamente con Metbeans il JForm, osservare le aree con fondo grigio, non sono più editabili. Se notate nel metoto initComponent() non troviamo nessuna impostazione riguardante il tipo di layout manager e quindi viene impostato automaticamente quello di default ossia il "Border Layout", questo è importante per far si che le dimensioni del JComponent di default 0x0 vengano estese alle dimensioni della finestra. Ho omesso il codice che imposta il look end feel.

Se andiamo a vedere in initComponent vediamo che il fondo era stato impostato al colore giallo (255,255,0), ma visualizzava il solito grigio, come se questa impostazione non avesse effetto. Nel metodo run ho di nuovo impostato il colore a bianco ma attenzione l'ho fatto richiamando il contetPane:

f.getContentPane().setBackground(Color.WHITE);

se lo avessi impostato così: f.setBackground(Color.WHITE); non avrebbe funzionato. Questo perché il JFrame ha molti strati quello più profondo è il rootPane che rimane sotto il contetPane. Il colre si imposta ma rimane nascosto. Bisogna impostare il colore di fondo sul contentPane richiamandolo, allora si rende visibile (riga 55).

 

Versione n 3:

Graf_03.java

 3 import java.awt.*;
 4 import javax.swing.*;
 5 
 6 public class Graf_03 extends JFrame {
 7 
 8     public Graf_03() {
 9         setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
10         setTitle("Disegna un ellisse (Graf_03)");
11         getContentPane().setBackground(Color.WHITE);
12         setBounds(800, 500, 300, 250);
13         add(new MyComp());
14         setVisible(true);
15         //pack();
16     }                     
17 
18     public static void main(String args[]) {
19 
20         /* Create and display the form */
21         java.awt.EventQueue.invokeLater(new Runnable() {
22             public void run() {
23                new Graf_03();                         
24             }
25         });
26     }              
27 }         

 

MyComp.java

 3 import java.awt.BasicStroke;
 4 import java.awt.Graphics;
 5 import java.awt.Graphics2D;
 6 import java.awt.geom.Ellipse2D;
 7 import javax.swing.JComponent;
 8 
 9 public class MyComp extends JComponent{    
10     final BasicStroke spessore = new BasicStroke(5.0f);
11     public void paintComponent(Graphics g) { 
12        super.paintComponent(g);
13        Graphics2D g2d =(Graphics2D)g;
14        g2d.setStroke(spessore);
15        Ellipse2D.Double fc = new Ellipse2D.Double(95,30,100,150);
16        g2d.draw(fc);
17     }
18 }

 

Discussione:

Il primo file Graf_03 contiene la classe principale che estende il JFrame, ma l'abbiamo molto sfoltita, abbiamo eliminato il metodo initComponent() e abbiamo inserito tutto direttamente nel costruttore.

Attenzione alla riga 15, se decommentiamo pack(); vedrete una finestra collassata in quanto l'ellisse che abbiamo disegnato fa parte del fondo, non una fisicità, non si oppone al collasso. L'altra esegue il disegno nel modo consueto.

 

Variante n. 4

Graf_04.java

 3 import java.awt.*;
 4 import java.awt.geom.*;
 5 import javax.swing.*;
 6 
 7 public class Graf_04 { 
 8     public static void main(String[] args) {
 9         SwingUtilities.invokeLater(new Runnable() {
10             public void run() {
11                 JForm f = new JForm(); 
12                 f.add(new MyComp());
13             }            
14        });
15     }
16 }
17 class MyComp extends JComponent{    
18     final BasicStroke spessore = new BasicStroke(5.0f);
19     public void paintComponent(Graphics g) { 
20        super.paintComponent(g);
21        Graphics2D g2d =(Graphics2D)g;
22        g2d.setStroke(spessore);
23        Ellipse2D.Double fc = new Ellipse2D.Double(95,30,100,150);
24        g2d.draw(fc);
25     }
26 }         

 

JForm.java

  3 import java.awt.*;
 4 import javax.swing.*;
 5 
 6 public class JForm extends JFrame {
 7         public JForm(){
 8             setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
 9             setTitle("Disegna un ellisse (Graf_05)");
10             getContentPane().setBackground(Color.WHITE);
11             setBounds(800, 500, 300, 250);
12             setVisible(true);                
13         }      
14 }             

 

Discussione:

Si commenta da solo

 

Variante n. 5

Graf_05.java

 3 import javax.swing.*;
 4  
 5 public class Graf_05{  
 6     public static void main(String[] args) {
 7        SwingUtilities.invokeLater(new Runnable() {
 8            public void run() {
 9              new JForm();                     
10             }            
11        });
12     }
13 } 

 

JForm.java

 3 import java.awt.*;
 4 import javax.swing.*;
 5 
 6 public class JForm extends JFrame {
 7         public JForm(){
 8             setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
 9             setTitle("Disegna un ellisse (Graf_05)");
10             getContentPane().setBackground(Color.WHITE);
11             setBounds(800, 500, 300, 250);
12             add(new MyComp());
13             setVisible(true);                
14         }      
15 } 

 

MyComp.java

 3 import java.awt.BasicStroke;
 4 import java.awt.Graphics;
 5 import java.awt.Graphics2D;
 6 import java.awt.geom.Ellipse2D;
 7 import javax.swing.JComponent;
 8 
 9 public class MyComp extends JComponent{    
10     final BasicStroke spessore = new BasicStroke(5.0f);
11     public void paintComponent(Graphics g) { 
12        super.paintComponent(g);
13        Graphics2D g2d =(Graphics2D)g;
14        g2d.setStroke(spessore);
15        Ellipse2D.Double fc = new Ellipse2D.Double(95,30,100,150);
16        g2d.draw(fc);
17     }
18 }         

 

Discussione:

Primo file Graf_05.java classe principale eseguibile che si limita ad istanziare la classe JForm, il secondo file la JForm.java la classe JForm che oltre a definire tutti i parametri del form aggiunge anche il JComponent (MyComp), ed infine l'ultimo file MyComp.java che come al solito si occupa del disegno.

Ormai abbiamo visto tutte le combinazioni possibili a Voi la scelta di quella più adatta.

 

Download:

Tipo Var 1 Var 2 Var 3 Var 4 Var 5

Progetto Netbeans

Graf_01.zip

Graf_02.zip

Gra_f03.zip

Graf_04.zip

Graf_05.zip

Note: se non si usa Netbeans basterà utilizzare i sorgenti all'interno della direttory src/Graf_0x