Le Immaggini
Passo1 Passo2 Passo3
Obiettivo: Ruotare un'immagine intorno al suo centro

Documentazione: |
 |
Inserire immagini o disegnare con ImageIcon |
 |
Tutorial GRAFICA BI-DIMENSIONALE IN JAVA
|
|
Ora eseguiremo un'altro passetto avanti, faremo in modo che la manopola dell'esercizio precedente possa ruotare intorno al suo centro, cambiare il senso e la velocità di rotazione, girare in continuo o passo passo, impostare il passo in gradi centesimali.
Classe principale eseguibile
1 package manov1;
2
3 import java.awt.*;
4 import java.awt.event.ActionEvent;
5 import java.awt.event.ActionListener;
6 import java.awt.event.ItemEvent;
7 import java.awt.event.ItemListener;
8 import javax.swing.event.ChangeListener;
9 import javax.swing.event.ChangeEvent;
10 import java.util.Locale;
11 import javax.swing.*;
12
13 public class ManoV1 extends JFrame{
14 private JButton jb,jx;
15 private JToggleButton jg;
16 private JSpinner jt,js;
17 private JLabel jv,jgra,jval;
18 private int flg=0,lx,ly,lw,mx,my,cox,coy,vel=10;
19 private double gra=0,inc=0.5;
20 private Timer tm;
21 private String val;
22 private MyIcon mn;
23
24 public ManoV1() {
25 Locale.setDefault(Locale.Category.FORMAT, Locale.ENGLISH);
26
27 setTitle("Visualizzazione Immagine");
28 setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
29 getContentPane().setBackground(Color.white);
30 setLayout(null);
31 setSize(550, 500);
32 setLocationRelativeTo(null);
33
34
35 jb = new JButton("Passo");
36 jb.addActionListener(new ActionListener() {
37 public void actionPerformed(ActionEvent evt) {
38 tm.stop();
39 jg.setText("Start");
40 jg.setSelected(false);
41 varia();
42 mn.setGra(gra);
43 }
44 });
45 jb.setBounds(15, 10, 80, 23);
46 add(jb);
47
48
49 jx = new JButton("Direzione");
50 jx.addActionListener(new ActionListener() {
51 public void actionPerformed(ActionEvent evt) {
52 if(flg==1)flg=0; else flg=1;
53 }
54 });
55 jx.setBounds(100, 10, 80, 23);
56 add(jx);
57
58
59 js = new JSpinner();
60 js.setLocale(Locale.ENGLISH);
61 js.setModel(new SpinnerNumberModel(inc,0.0d,360.0d,0.1d));
62 js.setFont(new java.awt.Font("Tahoma", 1, 14));
63 js.addChangeListener(new ChangeListener() {
64 public void stateChanged(ChangeEvent evt) {
65 inc = Double.parseDouble (""+js.getValue());
66 }
67 });
68 js.setBounds(240, 9, 60, 25);
69 add(js);
70
71
72 jt = new JSpinner(new SpinnerNumberModel(vel,1,2000,5));
73 jt.setFont(new java.awt.Font("Tahoma", 1, 14));
74 jt.addChangeListener(new ChangeListener() {
75 public void stateChanged(ChangeEvent evt) {
76 vel = Integer.parseInt(""+jt.getValue());
77 tm.setDelay(vel);
78 tm.start();
79 }
80 });
81 jt.setBounds(360, 9,75, 25);
82 add(jt);
83
84
85 jg = new JToggleButton("Start");
86 jg.addItemListener(new ItemListener() {
87 public void itemStateChanged(ItemEvent evt) {
88 int status = evt.getStateChange();
89 if(status == ItemEvent.SELECTED){
90 jg.setText("Stop");
91 tm.start();
92 }else{
93 jg.setText("Start");
94 tm.stop();
95 }
96 }
97 });
98 jg.setBounds(440, 10, 80, 23);
99 add(jg);
100
101
102 jgra = new JLabel("inc-gra");
103 jgra.setFont(new Font("Tahoma", 1, 14));
104 jgra.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
105 add(jgra);
106 jgra.setBounds(188, 10, 50, 20);
107
108
109 jval = new JLabel("inc-val");
110 jval.setFont(new Font("Tahoma", 1, 14));
111 jval.setHorizontalAlignment(javax.swing.SwingConstants.RIGHT);
112 add(jval);
113 jval.setBounds(308, 10, 50, 20);
114
115
116 jv = new JLabel("0");
117 jv.setFont(new Font("Digital-7Mono", 1, 50));
118 jv.setForeground(Color.RED);
119 jv.setHorizontalAlignment(javax.swing.SwingConstants.CENTER);
120 jv.setText("0");
121 add(jv);
122
123
124 mn = new MyIcon();
125 mn.setLocation(103, 86);
126 add(mn);
127
128 setVisible(true);
129
130 cox=getContentPane().getSize().width;
131 coy=getContentPane().getSize().height;
132 mx=(cox-(mn.getWp()))/2;
133 my=(coy-(mn.getHp()))/2;
134 mn.setLocation(mx, my);
135 mn.setLocation(mx, my);
136
137
138 lw=200;
139 lx=(cox-lw)/2;
140 ly=(coy-60);
141 jv.setBounds(lx, ly, 200, 40);
142
143
144 tm = new Timer(vel,new ActionListener() {
145 public void actionPerformed(ActionEvent evt) {
146 varia();
147 }
148 });
149 }
150
151
152 private void varia(){
153 if(flg==0){
154 gra=gra+inc;
155 if(gra>360){
156 gra=gra-360;
157 }
158 }else{
159 if(gra==0){
160 gra=360-inc;
161 }else{
162 gra=gra-inc;
163 if(gra<0){
164 gra=gra+360;
165 }
166 }
167 }
168
169 mn.setGra(gra);
170 val=String.format(new Locale("en"),"%1$.2f",gra);
171 jv.setText(val);
172 }
173
174 public static void main(String [] args){
175
176 new ManoV1();
177 }
178 }
|
Classe esterna che disegna la manopola
Classe: MyIcon |

|
1 package manov1;
2
3 import java.awt.Graphics;
4 import java.awt.Graphics2D;
5 import java.net.*;
6 import javax.swing.*;
7
8 class MyIcon extends JComponent{
9 private double gra=0;
10 private ImageIcon icon;
11 private int wP,hP,xC,yC;
12
13 public MyIcon(){
14 icon = createImageIcon("/image/mano1.png");
15 wP = icon.getIconWidth();
16 hP = icon.getIconHeight();
17 setSize(wP, hP);
18 }
19 public void paintComponent(Graphics g) {
20 super.paintComponent(g);
21 Graphics2D g2d =(Graphics2D)g;
22
23 xC =wP/2;
24 yC =hP/2;
25
26 g2d.translate(xC,yC);
27 g2d.rotate(Math.toRadians(gra));
28 g2d.translate(-xC,-yC);
29
30 icon.paintIcon(this, g2d, 0, 0);
31 }
32 public void setGra(double gr){
33 gra=gr;
34 repaint();
35 }
36 public int getWp(){
37 return(wP);
38 }
39
40 public int getHp(){
41 return(hP);
42 }
43
44 private ImageIcon createImageIcon(String path) {
45 URL imgURL = getClass().getResource(path);
46 if (imgURL != null) {
47 return new ImageIcon(imgURL);
48 } else {
49 System.err.println("Non è possibile trovare il file: " + path);
50 return null;
51 }
52 }
53 } |
Discussione:
Occupiamici della classe MyIcon a parte le considerazioni riguardanti il caricamento e la visualizzazione già trattate qui passiamo direttamente alla sezione di codice che si interessa della rotazione 19-31. Si prelevano le dimensioni dell'immagine (wP e hP) 15-16, nella 17 si assegnano le stesse dimenzioni a MyIcon (il componente).Poi si calcolano le coordinate xC e yC del centro dell'immagine righe 23-24. Dalla 26 alla 28 realizziamo la rotazione. Importante rispettare questo preciso ordine, il codice verrà eseguito in realtà al contrario ossia prima la 28 poi la 27 ed infine la 26. Con la 28 il centro della nostra immagine (la manopola) viene spostato all'origine degli assi del JComponent MyIcon, intorno al quale avviene sempre la rotazione. Una volta in questa posizione la 27 esegue la rotazione dell'angolo desiderato (notare la conversione da angolo centesimale (gradi,centesimi) a radianti (360=2Pi)) poi l'immagine viene riportata alla posizione originale ossia il centro di MyIcon. Alla riga 30 avviene il miracolo icon.paintIcon(this, g2d, x, y); ciò che avviene all'interno dovrebbe essere questo: il metodo paintIcon della nostra immagine andrà per prima cosa a disegnarla dove? all'interno del contenitore this (MyICon) nella posizione x=0 e y=0 (l'angolo in alto a sinistra di MyIcon) poi darà via al contesto grafico g2d con le relative trasformazioni. Ultima considerazione il metodo setGra che riceve come parametro l'angolo a cui ruotare la manopola che verrà inserito nella variabile privata gra, dopo di che lancia il metodo repaint che prima cancellerà la vecchia manopola per ripetere poi di nuovo il disegno ma con il nuovo angolo.
Ora ci dedichiamo alla classe principale:
Questa è la sezione delle dichiarazioni delle variabili
14 private JButton jb,jx;
15 private JToggleButton jg;
16 private JSpinner jt,js;
17 private JLabel jv,jgra,jval;
18 private int flg=0,lx,ly,lw,mx,my,cox,coy,vel=10;
19 private double gra=0,inc=0.5;
20 private Timer tm;
21 private String val;
22 private MyIcon mn; |
Creazione del Frame.
interessanti sono le righe 31 e 32 perché ci permettono in modo sintetico di collocare la nostra finestra (JFrame) al centro dello schermo. Però attenzione, affinchè funzioni bisogna prima impostare le dimensioni poi setLocationRelativeTo(null); non viceversa. Questo perchè fino a che non viene dimensionata la finestra non ha dimensioni.
27 setTitle("Visualizzazione Immagine");
28 setDefaultCloseOperation (JFrame.EXIT_ON_CLOSE);
29 getContentPane().setBackground(Color.white);
30 setLayout(null);
31 setSize(550, 500);
32 setLocationRelativeTo(null); |
Notare la linea 25:
25 Locale.setDefault(Locale.Category.FORMAT, Locale.ENGLISH); |
Questa imposta la formattazione dei numeri, date ecc. nel formato inglese ad es. i numeri vengono rappresentati così 12,345.00 mentre in europa siamo abituati a 12.345,00.
- Blocco 34-44 Spingendo il pulsante Passo
- 38 fermiamo il movimento della manopola
- 39-40 resettiamo l'interruttore Start, etichetta a "Start" e lo rendiamo inattivo
- 41 eseguiamo il metodo varia() che si preoccupa di gestire la posizione corretta degli angoli. Con un po di pazienza sarete in grado di capire l'algoritmo.
- 42 preparato il giusto angolo gra lo si da in pasto al metodo setGra() che penserà a ridisegnare la manopola nella nuova posizione.
34
35 jb = new JButton("Passo");
36 jb.addActionListener(new ActionListener() {
37 public void actionPerformed(ActionEvent evt) {
38 tm.stop();
39 jg.setText("Start");
40 jg.setSelected(false);
41 varia();
42 mn.setGra(gra);
43 }
44 }); |
Spingendo il pulsante direzione non facciamo altro che invertire il flag che nel metodo varia() srà responsabile del senso di rotazione:
48
49 jx = new JButton("Direzione");
50 jx.addActionListener(new ActionListener() {
51 public void actionPerformed(ActionEvent evt) {
52 if(flg==1)flg=0; else flg=1;
53 }
54 });
55 jx.setBounds(100, 10, 80, 23);
56 add(jx); |
Nel nostro caso lo spinner viene usato per incrementare o decrementare dei valori numerici. Ma è possibile utilizzarlo per altri scopi vedi approfondimento qui. Per impostarlo bisogna settare il relativo spinnerModel e trattandosi di valori numerici lo SpinnerNumberModel che ha 4 parametri. Usiamo per i gradi il costruttore con parametri Double, dove il primo indica il valore di partenza, il secondo il valore minimo, il terzo il valore massimo e l'ultimo l'incremento/decremento che deve avere. Ogni qual volta clicchiamo sulle freccette viene lanciato un evento del tipo ChangeEvent che l'ascoltatore associato processera nel metodo stateChanged . Qui non si fa altre che leggere il valore impostato nel textField e dopo averlo convertito in double inserirlo nella variabile inc.
58
59 js = new JSpinner();
60 js.setLocale(Locale.ENGLISH);
61 js.setModel(new SpinnerNumberModel(inc,0.0d,360.0d,0.1d));
62 js.setFont(new java.awt.Font("Tahoma", 1, 14));
63 js.addChangeListener(new ChangeListener() {
64 public void stateChanged(ChangeEvent evt) {
65 inc = Double.parseDouble (""+js.getValue());
66 }
67 });
68 js.setBounds(240, 9, 60, 25);
69 add(js);
70
71
72 jt = new JSpinner(new SpinnerNumberModel(vel,1,2000,5));
73 jt.setFont(new java.awt.Font("Tahoma", 1, 14));
74 jt.addChangeListener(new ChangeListener() {
75 public void stateChanged(ChangeEvent evt) {
76 vel = Integer.parseInt(""+jt.getValue());
77 tm.setDelay(vel);
78 tm.start();
79 }
80 });
81 jt.setBounds(360, 9,75, 25);
82 add(jt); |
Questo interruttore se schiacciato fa partire il timer, se rilasciato lo fermerà:
84
85 jg = new JToggleButton("Start");
86 jg.addItemListener(new ItemListener() {
87 public void itemStateChanged(ItemEvent evt) {
88 int status = evt.getStateChange();
89 if(status == ItemEvent.SELECTED){
90 jg.setText("Stop");
91 tm.start();
92 }else{
93 jg.setText("Start");
94 tm.stop();
95 }
96 }
97 });
98 jg.setBounds(440, 10, 80, 23);
99 add(jg); |
- 101 - 121 vengono definite le etichette. Osservare ta tecnica di centratura di quello che visualizza i valori dell'angolo in variazione.
- 123 - 126 si disegna la manopola istanziando la classe MyIcom.
- 129 - 135 centra la manopola
- 137 - 141 centra l'etichetta
- 143 - 149 il timer.
Un'ultima considerazione. La formattazione degli spinner si basa su un formato europeo ossia i decimali vengono rappresentati con la virgola e le migliaia con il punto. Il formato Inglese invece rappresenta i decimali con il pumto e le migliaia con la virgola. Poiché trovo molto più comodo il formato inglese anche per il fatto che con esso possiamo digitare tutto sul tastierino. Per modificare la formattazione possiamo utilizzare due modalità:
Questa:
js = new JSpinner();
js.setModel(new SpinnerNumberModel(inc,0.0d,360.0d,0.1d));
JSpinner.NumberEditor ne = (JSpinner.NumberEditor) js.getEditor();
ne.getFormat().setDecimalFormatSymbols(new DecimalFormatSymbols(Locale.ENGLISH)); |
O questa:
Locale.setDefault(Locale.Category.FORMAT, Locale.ENGLISH); |
La prima coinvolge solo lo spinner la seconda è generale. La seconda è decisamente più pratica e simtetica La prima bisogna scriverla per ogni spider che si ha.
|