View Full Version : [Java] problema metodo paint(Graphics g)
Ho scritto quest'applicazione che, una volta schiacciato il bottone, permette di disegnare delle piccole X con colore casuale sul pannello, mandando ai JtextField X e Y le coordinate in cui si è cliccato sul pannello.
Tuttavia non capisco perchè non mi disegna le crocette sul pannello, che ho settato come opaco e su cui invoco la repaint(); (ho provato anche a mettere la validate(); prima, ma non cambia nullla)..non capisco cosa sbaglio...
package pacchetto;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
public class Prova extends JFrame{
public static void main(String [] args){
JFrame x=new JFrame();
x.setSize(new Dimension(400,400));
x.setResizable(true);
x.setContentPane(new Pannello());
x.setDefaultCloseOperation(EXIT_ON_CLOSE);
x.setVisible(true);
}
}
public class Pannello extends JPanel{
int red,green,blue,X,Y;
Random generatore=new Random(System.currentTimeMillis()/27);
JButton bottone;
JLabel stato,x,y;
JFormattedTextField uno,due;
JPanel pane;
public Pannello(){
this.setBackground(Color.LIGHT_GRAY);
this.setLayout(new GridBagLayout());
GridBagConstraints c=new GridBagConstraints();
bottone=new JButton("Cambia stato");
bottone.addActionListener(new Abilita());
c.weightx=1;
c.fill=GridBagConstraints.HORIZONTAL;
c.weightx=1;
c.weighty=0;
c.gridx=0;
c.gridy=0;
c.gridwidth=1;
add(bottone,c);
stato=new JLabel("");
c.gridx=1;
c.gridy=0;
c.gridwidth=1;
c.anchor=GridBagConstraints.CENTER;
c.insets.left=5;
add(stato,c);
x=new JLabel("X=");
c.gridx=2;
c.gridy=0;
c.anchor=GridBagConstraints.CENTER;
c.gridwidth=1;
c.weightx=1;
c.weighty=0;
c.insets.left=50;
add(x,c);
y=new JLabel("Y=");
c.gridx=2;
c.gridy=1;
c.gridwidth=1;
add(y,c);
uno=new JFormattedTextField();
c.gridx=3;
c.gridy=0;
c.gridwidth=1;
c.insets.left=5;
add(uno,c);
due=new JFormattedTextField();
c.gridx=3;
c.gridy=1;
c.gridwidth=1;
add(due,c);
pane=new JPanel();
pane.setBackground(Color.WHITE);
pane.setBorder(BorderFactory.createLineBorder(Color.BLACK));
pane.setOpaque(true);
c.fill=GridBagConstraints.BOTH;
c.gridx=0;
c.gridy=2;
c.gridwidth=4;
c.gridheight=1;
c.insets.top=5;
c.weightx=1;
c.weighty=1;
add(pane,c);
}
class Abilita implements ActionListener{
boolean isActive=false;
public void actionPerformed(ActionEvent e) {
if(isActive==false){
stato.setText("ATTIVATO");
isActive=true;
pane.addMouseListener(new Disegno());
}
else if(isActive==true){
isActive=false;
stato.setText("DISATTIVATO");
pane.removeMouseListener(pane.getMouseListeners()[0]);
uno.setText("");
due.setText("");
}
}
}
class Disegno implements MouseListener{
public void mouseClicked(MouseEvent e) {
X=e.getX(); Y=e.getY();
uno.setText(String.valueOf(X));
due.setText(String.valueOf(Y));
}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}
public void paint(Graphics g){
super.paint(g);
green=generatore.nextInt(255)+1;
red=generatore.nextInt(255)+1;
blue=generatore.nextInt(255)+1;
g.setColor(new Color(red,green,blue));
g.drawLine(X-5, Y+5, X+5, Y-5);
g.drawLine(X-5, Y-5, X+5, Y+5);
repaint();
}
}
se qualcuno fosse in grado di aiutarmi...
banryu79
02-07-2009, 14:06
Scusami, ma che ci fa un metodo paint() che prende un Graphics e ci disegna sopra dentro la classe Disegno che altro non è se non l'implementazione di un MouseListener?
Forse volevi fare l'override di paintComponent() del JPanel in cui vuoi eseguire il rendering... il MouseListener al massimo ascolterà, appunto, gli eventi del mouse che ti interessano per estrarre informazioni utili ai fini del tuo rendering (coordinate evento click del mouse, per esempio).
Mi pare tu abbia ancora le idee poco chiare su certi concetti: ti rilinko questo otiimo e breve tutorial (http://www.hwupgrade.it/forum/showthread.php?t=2005654)
il tutorial che hai linkato non parla mica del custom graphics...
il metodo paint(Graphics g); viene invocato ogni volta che clicco sul pannello pane. poichè il metodo paint non va mai richiamato direttamente, invoco il repaint su pane (le X vanno disegnate solo all'interno del pannello pane)...
e infatti cliccando sul pannello, dopo averlo attivato, riesco a mandare le nformazioni sulle coordinate ai textfield. ma perchè non disegna la croce???
class Disegno implements MouseListener{
public void mouseClicked(MouseEvent e) {
X=e.getX(); Y=e.getY();
uno.setText(String.valueOf(X));
due.setText(String.valueOf(Y));
pane.repaint();
}
da quel che scrivi tu mi pare che non sia corretto usare il metodo paint, ma allora come si fa a disegnare?? :muro:
banryu79
02-07-2009, 14:34
il tutorial che hai linkato non parla mica del custom graphics...
Vero, però io intendevo che ci sono altre cose da sistemare, per esempio nel main() quando lanci l'interfaccia lo fai dal thread corrente; sarebbe bene (anzi bisognerebbe proprio, sempre e comunque) farlo dall'EDT e in quel tutorial è praticamente la prima cosa spiegata ;)
da quel che scrivi tu mi pare che non sia corretto usare il metodo paint, ma allora come si fa a disegnare?? :muro:
Il fatto è che è sul JPanel che tu vuoi disegnare, ovvero su un componente grafico.
Un JPanel, in quanto componente grafico, estende JComponent, dal quale eredita il metodo paintComponent(Graphics g) che è il metodo che viene invocato durante il ciclo di rendering dei componenti Swing.
Quindi se tu definisci un tuo JPanel e vuoi renderizzare qualcosa sulla sua superficie devi fare l'ovveride di tale metodo, e definirne il corpo inserendo le tue operazioni di rendering (che tipicamente si fanno operando sul Graphics passato in ingresso).
Ci penserà poi il framework Swing a invocare paintComponet() ogni volta che riterrà neccessario aggiornare la grafica.
Tu puoi comunicare a Swing che ti interessa aggiornare la grafica del tal componente invocando il metodo repaint() sul componente specifico: sarà comunqe Swing a invocare dietro le quinte la paintComponent.
Ti posto i javadoc del metodo paintComponent della classe JComponent:
protected void paintComponent(Graphics g)
Calls the UI delegate's paint method, if the UI delegate is non-null. We pass the delegate a copy of the Graphics object to protect the rest of the paint code from irrevocable changes (for example, Graphics.translate).
If you override this in a subclass you should not make permanent changes to the passed in Graphics. For example, you should not alter the clip Rectangle or modify the transform. If you need to do these operations you may find it easier to create a new Graphics from the passed in Graphics and manipulate it. Further, if you do not invoker super's implementation you must honor the opaque property, that is if this component is opaque, you must completely fill in the background in a non-opaque color. If you do not honor the opaque property you will likely see visual artifacts.
The passed in Graphics object might have a transform other than the identify transform installed on it. In this case, you might get unexpected results if you cumulatively apply another transform.
Parameters:
g - the Graphics object to protect
banryu79
02-07-2009, 14:45
In pratica, dovresti sovvrascrivere il metodo paintComponet del JPanel sul quale disegni, con uno schema simile a questo:
class MioPannelloDiRendering extends JPanel
{
@Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
// comandi personalizzati di rendering sul Graphics...
}
}
Puoi leggerti qualcosa, se ti interessa:
- Painting in Swing(in italiano, slide di una dispensa universitaria) (http://users.dimi.uniud.it/~vitaliano.milanese/SlideOOP/Slide_16.pdf)
- Painting in AWT e Swing(roba della Sun, in inglese, consigliata) (http://java.sun.com/products/jfc/tsc/articles/painting/)
- Lesson: performing custom painting(tutorial della Sun, preso dal vasto The Really Big Index, in inglese, consigliato) (file:///C:/javaProjects/tutorials/BigIndex/tutorial/uiswing/painting/index.html)
ma quindi dovrei racchiudere pane in una classe, e specificare in essa il metodo paintComponent()?
del tipo
class Pane extends JPanel{
JPanel pane=new JPanel();
proteced void paintComponent(Graphics g){
blablabla;
}
}
e poi come lo aggiungo al pannello???
addComponent(new Pane()); ?
L'approccio che hai usato non è forse orotodosso ma, a parte due piccole incertezze, è tecnicamente corretto.
Le ragioni per cui non funziona sono due ma sono due piccole sviste.
La prima è che devi aggiungere il tuo ascoltatore di eventi Disegno al pannello che metti nel frame: la semplice dichiarazione non basta.
Il tuo metodo main diventa allora una cosa del genere:
public static void main(String [] args){
Pannello p = new Pannello();
p.addMouseListener(p.new Disegno());
JFrame x=new JFrame();
x.setSize(new Dimension(400,400));
x.setResizable(true);
x.setContentPane(p);
x.setDefaultCloseOperation(EXIT_ON_CLOSE);
x.setVisible(true);
}
La seconda svista è la posizione dell'invocazione "repaint()". L'hai messa nel metodo paint() ma l'invocazione di repaint() causa a sua volta l'invocazione di repaint() quindi hai un ciclo ricorsivo infinito.
Togli repaint() dal metodo paint() e metti repaint() come ultima invocazione el metodo mouseClicked della classe Disegno.
E tutto funzionerà.
Per ragioni di concorrenza dovresti prendere quello che hai nel metodo main metterlo nel run() di un'istanza di Runnable e poi passare quel Runnable al metodo invokeLater di EventQueue ma questa è un'altra storia.
banryu79
02-07-2009, 15:11
Postrsti usare una classe anonima per pane, così:
JPanel pane;
...
pane = new JPanel()
{
@Override protected void paintComponet(Graphics g);
{
super.paintComponet(g);
// qui inserisci il tuo codice di rendering...
}
};
banryu79
02-07-2009, 15:15
* edit
banryu79
02-07-2009, 15:18
Come non detto, ho mancato la lettura di una parentesi nel codice... chiedo venia :D
@cory: scusa se ti ho fatto confusione.
ho seguito quanto detto da PGI-Bis
sul posizionamento del repaint(); ok, era una cosa che avevo già notato e corretto.
modificando il main come dici tu però non cambia nulla...in sostanza, nella figura che segue, non mi disegna le croci sul pannello bianco..
http://img16.imageshack.us/img16/5968/immaginecqh.th.png (http://img16.imageshack.us/i/immaginecqh.png/)
Come non detto, ho mancato la lettura di una parentesi nel codice... chiedo venia :D
@cory: scusa se ti ho fatto confusione.
:D figurati...più che altro mi sembrava una soluzione troppo complicata e cervellotica, anche considerando il modo in cui mi hanno insegnato a programmare
EDIT: magari non c'entra una tega, ma io ve lo dico lo stesso..uso Netbeans e la classe Prova è in un file e quella Pannello in un altro..
Questo è il main:
public static void main(String [] args){
Pannello p = new Pannello();
p.addMouseListener(p.new Disegno());
JFrame x=new JFrame();
x.setSize(new Dimension(400,400));
x.setResizable(true);
x.setContentPane(p);
x.setDefaultCloseOperation(EXIT_ON_CLOSE);
x.setVisible(true);
}
questo è il paint:
public void paint(Graphics g){
super.paint(g);
green=generatore.nextInt(255)+1;
red=generatore.nextInt(255)+1;
blue=generatore.nextInt(255)+1;
g.setColor(new Color(red,green,blue));
g.drawLine(X-5, Y+5, X+5, Y-5);
g.drawLine(X-5, Y-5, X+5, Y+5);
//repaint();
}
e questo il mouseClicked:
public void mouseClicked(MouseEvent e) {
X=e.getX(); Y=e.getY();
uno.setText(String.valueOf(X));
due.setText(String.valueOf(Y));
repaint();
}
ho apportato quella modifica al main ma non cambia nulla...
Non è possibile.
Questo è il codice completo del programma.
package test;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
public class Prova extends JFrame{
public static void main(String [] args){
Pannello p = new Pannello();
p.addMouseListener(p.new Disegno());
JFrame x=new JFrame();
x.setSize(new Dimension(400,400));
x.setResizable(true);
x.setContentPane(p);
x.setDefaultCloseOperation(EXIT_ON_CLOSE);
x.setVisible(true);
}
}
class Pannello extends JPanel{
int red,green,blue,X,Y;
Random generatore=new Random(System.currentTimeMillis()/27);
JButton bottone;
JLabel stato,x,y;
JFormattedTextField uno,due;
JPanel pane;
public Pannello(){
this.setBackground(Color.LIGHT_GRAY);
this.setLayout(new GridBagLayout());
GridBagConstraints c=new GridBagConstraints();
bottone=new JButton("Cambia stato");
bottone.addActionListener(new Abilita());
c.weightx=1;
c.fill=GridBagConstraints.HORIZONTAL;
c.weightx=1;
c.weighty=0;
c.gridx=0;
c.gridy=0;
c.gridwidth=1;
add(bottone,c);
stato=new JLabel("");
c.gridx=1;
c.gridy=0;
c.gridwidth=1;
c.anchor=GridBagConstraints.CENTER;
c.insets.left=5;
add(stato,c);
x=new JLabel("X=");
c.gridx=2;
c.gridy=0;
c.anchor=GridBagConstraints.CENTER;
c.gridwidth=1;
c.weightx=1;
c.weighty=0;
c.insets.left=50;
add(x,c);
y=new JLabel("Y=");
c.gridx=2;
c.gridy=1;
c.gridwidth=1;
add(y,c);
uno=new JFormattedTextField();
c.gridx=3;
c.gridy=0;
c.gridwidth=1;
c.insets.left=5;
add(uno,c);
due=new JFormattedTextField();
c.gridx=3;
c.gridy=1;
c.gridwidth=1;
add(due,c);
pane=new JPanel();
pane.setBackground(Color.WHITE);
pane.setBorder(BorderFactory.createLineBorder(Color.BLACK));
pane.setOpaque(true);
c.fill=GridBagConstraints.BOTH;
c.gridx=0;
c.gridy=2;
c.gridwidth=4;
c.gridheight=1;
c.insets.top=5;
c.weightx=1;
c.weighty=1;
add(pane,c);
}
class Abilita implements ActionListener{
boolean isActive=false;
public void actionPerformed(ActionEvent e) {
if(isActive==false){
stato.setText("ATTIVATO");
isActive=true;
pane.addMouseListener(new Disegno());
}
else if(isActive==true){
isActive=false;
stato.setText("DISATTIVATO");
pane.removeMouseListener(pane.getMouseListeners()[0]);
uno.setText("");
due.setText("");
}
}
}
class Disegno implements MouseListener{
public void mouseClicked(MouseEvent e) {
X=e.getX(); Y=e.getY();
uno.setText(String.valueOf(X));
due.setText(String.valueOf(Y));
repaint();
}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}
public void paint(Graphics g){
super.paint(g);
green=generatore.nextInt(255)+1;
red=generatore.nextInt(255)+1;
blue=generatore.nextInt(255)+1;
g.setColor(new Color(red,green,blue));
g.drawLine(X-5, Y+5, X+5, Y-5);
g.drawLine(X-5, Y-5, X+5, Y+5);
//repaint();
}
}
Se ridimensioni la finestra dopo aver fatto un po' di click compare la crocetta?
dopo aver cliccato a caso per un bel po', anche senza ridimensionare, m'è comparsa qualche X a fianco del bottone :confused: :confused: :confused:
incolla il codice che hai.
package pacchetto;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.util.*;
public class Prova extends JFrame{
public static void main(String [] args){
JFrame x=new JFrame();
x.setSize(new Dimension(400,400));
x.setResizable(true);
x.setContentPane(new Pannello());
x.setDefaultCloseOperation(EXIT_ON_CLOSE);
x.setVisible(true);
}
}
public class Pannello extends JPanel{
int red,green,blue,X,Y;
Random generatore=new Random(System.currentTimeMillis()/27);
JButton bottone;
JLabel stato,x,y;
JFormattedTextField uno,due;
JPanel pane;
public Pannello(){
this.setBackground(Color.LIGHT_GRAY);
this.setLayout(new GridBagLayout());
GridBagConstraints c=new GridBagConstraints();
bottone=new JButton("Cambia stato");
bottone.addActionListener(new Abilita());
c.weightx=1;
c.fill=GridBagConstraints.HORIZONTAL;
c.weightx=1;
c.weighty=0;
c.gridx=0;
c.gridy=0;
c.gridwidth=1;
add(bottone,c);
stato=new JLabel("");
c.gridx=1;
c.gridy=0;
c.gridwidth=1;
c.anchor=GridBagConstraints.CENTER;
c.insets.left=5;
add(stato,c);
x=new JLabel("X=");
c.gridx=2;
c.gridy=0;
c.anchor=GridBagConstraints.CENTER;
c.gridwidth=1;
c.weightx=1;
c.weighty=0;
c.insets.left=50;
add(x,c);
y=new JLabel("Y=");
c.gridx=2;
c.gridy=1;
c.gridwidth=1;
add(y,c);
uno=new JFormattedTextField();
c.gridx=3;
c.gridy=0;
c.gridwidth=1;
c.insets.left=5;
add(uno,c);
due=new JFormattedTextField();
c.gridx=3;
c.gridy=1;
c.gridwidth=1;
add(due,c);
pane=new JPanel();
pane.setBorder(BorderFactory.createLineBorder(Color.BLACK));
c.fill=GridBagConstraints.BOTH;
c.gridx=0;
c.gridy=2;
c.gridwidth=4;
c.gridheight=1;
c.insets.top=5;
c.insets.left=0;
c.weightx=1;
c.weighty=1;
add(pane,c);
}
class Abilita implements ActionListener{
boolean isActive=false;
public void actionPerformed(ActionEvent e) {
if(isActive==false){
stato.setText("ATTIVATO");
isActive=true;
pane.addMouseListener(new Disegno());
}
else if(isActive==true){
isActive=false;
stato.setText("DISATTIVATO");
pane.removeMouseListener(pane.getMouseListeners()[0]);
uno.setText("");
due.setText("");
}
}
}
class Disegno implements MouseListener{
public void mouseClicked(MouseEvent e) {
X=e.getX(); Y=e.getY();
uno.setText(String.valueOf(X));
due.setText(String.valueOf(Y));
repaint();
}
public void mousePressed(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {}
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
}
protected void paintComponent(Graphics g){
super.paintComponent(g);
green=generatore.nextInt(256)+1;
red=generatore.nextInt(256)+1;
blue=generatore.nextInt(256)+1;
g.setColor(new Color(red,green,blue));
g.drawLine(X-5, Y+5, X+5, Y-5);
g.drawLine(X-5, Y-5, X+5, Y+5);
}
}
lasciandolo così, mi disegna le X sul pannello in generale..nel senso che se attivo il JPanel e clicco in un dato punto (x,y) di pane mi disegna la X nel punto (x,y) del frame.
Allora ho provato a sostituire il repaint(); del MouseListener con pane.repaint(); ma
1. nel JPanel pane non disegna nulla, pur inviando le giuste coordinate ai textfield
2. a furia di cliccare a caso e per un bel po' di tempo mi spunta qualche X sulla parte sopra il pannello, senza nessuna logica precisa..
banryu79
02-07-2009, 16:13
:D figurati...più che altro mi sembrava una soluzione troppo complicata e cervellotica, anche considerando il modo in cui mi hanno insegnato a programmare
A dire il vero la soluzione che ti ho proposto (eseguire l'override del metodo paintComponet di un JPanel) è quella canonica.
Forse ti ha confuso il fatto che invece di dichiarare esplicitamente una classe che estende JPanel e poi fa l'override, io ti ho postato l'esempio con una classe anonima. Ma è canonico pure quello! :D
Invece il resto dei miei vaneggiamenti era dovuto al fatto che leggendo il tuo codice avevo perso per starda un parentesi graffa e avevo erroneamente interpretato il codice pensando che avessi inserito il metodo paint dentro la classe Disegno... tutto qua :)
256 + 1 non va bene, il massimo per un componente intero di un Color è 255. Se ti capita un 255 ti salta fuori 256 con relativa eccezione.
Non devi collegare il mouseListener a pane ma a "pannello". Metti un Pannello.this.addMouseListener nell'actionPerformed al posto di pane.addMouseListener.
Un conto è sovrascrivere paint un conto è sovrascrivere paintComponent. Se il pannello su cui disegni contiene altri componenti, come nel tuo caso, allora devi usare paint altrimenti i figli disegnano sopra al tuo paintComponent e non vedi una mazza.
Capita perchè paintComponent viene prima di paintChildren.
Al che mi viene il dubbio: ma tu vuoi mettere le crocette sul pannello o su un componente all'interno di quel pannello?
banryu79
02-07-2009, 16:19
Come prima correzione del codice proporrei di inserire l'operazione di realizzazione del frame nella event queue.
Sistemato quello, prima di tutto il resto, poi si va a cascata. Altrimenti è difficile fare ipotesi sulla natura delle bizzarrie grafiche, giusto?
Al che mi viene il dubbio: ma tu vuoi mettere le crocette sul pannello o su un componente all'interno di quel pannello?
SUL PANNELLO PANE, QUELLO BIANCO CHE SI VEDE NELLA FOTO!!
No le bizzarrie sono evidenti. Il problema è che anzichè ridurle qui aumentano. Tipo: "cacchio quella roba lì era giusta, aspetta che adesso la correggo sbagliata" :D
ho bisogno di una pausa!!
inizialmente comunque usavo paint, non paint Component
banryu79
02-07-2009, 16:31
Mea culpa che ti ho fatto casino; mi ritiro con disonore :D
SUL PANNELLO PANE, QUELLO BIANCO CHE SI VEDE NELLA FOTO!!
Capirai che non è che le tue intenzioni siano cristalline se mi sovrascrivi il metodo paint di Pannello e aggiungi il mouseListener a pane.
Se vuoi disegnare su "pane" sarà il metodo paintComponent di pane ad essere sovrascritto.
Capirai che non è che le tue intenzioni siano cristalline se mi sovrascrivi il metodo paint di Pannello e aggiungi il mouseListener a pane.
Se vuoi disegnare su "pane" sarà il metodo paintComponent di pane ad essere sovrascritto.
ollà, ecco l'inghippo! sovrascrivo il metodo della classe sbagliata! il problema è: come sovrascrivo il metodo di pane?
be' non hai molte opzioni: o crei una sottoclasse o... crei una sottoclasse.
sarà lo stress, la stanchezza, l'incazzatura...ma non ho idea di come si faccia una sottoclasse...
Hai bisogno di una pausa. Pannello è una sottoclasse? Lo stesso devi fare per "pane":
public class MyCanvas extends JPanel {
@Override
protected void paintComponent(Graphics g) {...eccetera}
}
e al posto di:
JPanel pane = new JPanel()
ci metterai:
JPanel pane = new MyCanvas();
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.