PDA

View Full Version : [JAVA] Dimensioni JFrame


fefott
20-09-2006, 14:51
Sto cercando il modo di fare ingrandire una frame una volta che è già stata creata...
Ok, lo so che detta così non si capisce molto..diciamo che ho creato
un gioco e alla fine di ogni livello il gioco deve ingrandirsi anche se
per il momento si ingrandisce il pannello ma non il contenitore (la frame) e dunque il tutto viene visualizzato "spezzato"..
Secondo voi è possibile??
Ho già provato a inserire un ulteriore setSize ma senza grandi
risultati.
Aspetto con impazienza una vostra risposta.

MEMon
20-09-2006, 15:24
Bhe a ridimensionare un Jframe non ci dovrebbero essere grossi problemi, usi un setSize come hai già detto te.
Cosa devi ottenere di preciso e cosa invece non ottieni?

fefott
21-09-2006, 07:04
NO, non funziona...
il problema è che se faccio partire il gioco direttamente da livelli successivi la frame appare più grande dell'originale ma partendo dal primo e aspettando che si ingrandisce non lo fa.
Prova a vedere se costruendo una frame e dando invio questa cambia le sue dimensioni.
Come non detto ho appena provato a costruirla e funziona, dunque è un problema di dove ho messo la variabile che incrementa le dimensioni.

fefott
21-09-2006, 09:40
Ora che sono riuscita a far aumentare la frame ho un altro piccolo problema. Per spiegarlo allego un immagine...poichè è meglio di 1000 parole.
Aspetto suggerimenti.

MEMon
21-09-2006, 11:34
Nn vedo l'immagine sec me ti sei dimenticata di allegarla :D

fefott
21-09-2006, 14:24
Ci sto provando tutta la mattina...ma sembra non voler allegare nessuna immagine...ad ogni modo puoi vederla da qui: http://forum.html.it/forum/showthread.php?threadid=1032237

MEMon
21-09-2006, 14:27
In pratica hai il JFrame + grande dell'area su cui disegni?
Dimmi, su cosa disegni? Usi un JPanel?

fefott
21-09-2006, 15:42
NO, la frame ora è della dimensione giusta per ospitare l'intero labirinto che come si vede dalla figura appare spezzato..in pratica è più grande solo non vedo il pezzo aggiunto poichè oscurato dal colore di sfondo della frame.
SI, disegno su un JPanel.

MEMon
21-09-2006, 16:41
Dalle tue parole sembra che hai la frame nera, quindi il tuo jpanel non è delle dimensioni del jframe.
Per capirci, fammi uno screen con il jpanel di colore diverso dal frame!

Se invece è il tuo Jpanel ad essere nero, e come dici te è delle stesse dimensioni del jframe, allora bisogna che mi dici come disegni il labirinto, cioè voglio sapere se per disegnare l'area di gioco utilizzi qualche variabile che dipende dalle dimensioni del jframe.

TellaspallaBob
21-09-2006, 22:17
Allora io non ho molto ben capito quello che vuoi fare, perchè per dimensionare un Frame, basta fare un setSize o un setBounds, non capisco se il problema è l'auto riposizionamento dei componeti perchè in tal senso basta usare i LayoutManager; quindi penso che il tuo sia un problema di disegno grafico.
Ti faccio una piccola teoria sulle swing e su alcuni oggetti grafici AWT e su come si usano per disegnare.

Una JFrame è un oggetto complesso fatto da JRootPane, JLayeredPane GlassPane e soprattuto l'unico che a te interessa realmente il ContentPane...

Il ContentPane (è solo un concetto, in realta di solito è un JPanel o un oggetto che deriva da un JComponent o un Container AWT) è quello che appunto ospita tutti i componenti del frame.

Velocemente è quello l'oggetto che tu devi prendere in esame per il disegno.

Ma andiamo oltre....
Se devi disegnare qualcosa estendi un JPanel e ridefinisci il metodo paintComponent(Graphics g)

Tieni presente che sempre la tua prima istruzione che in tal metodo dovrà essere una chiamata al metodo padre quindi: super.paintComponent(g)
questo permetterà al pannello di eseguire correttamente le sue operazioni di refresh di base.

Tale metodo viene sempre richiamato dal Thread Grafico di Java qualora servisse: sposti la schermata, la ridimensioni, la deiconizzi etc. e dal metodo repaint() che puoi richiamare a tuo piacimento.

In tal metodo puoi sapere sempre la dimensione del pannello su cui stai andando a disegnare

protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Dimension dimPanel = getPreferredSize();

// Ora fai tutto quello che vuoi con l'oggetto Grafico.
}

Come vedi ho usato il getPreferredSize perchè nei LayouManager i componenti non sono soggetti al setSize ma al preferred, minimum, e maximum size.

un consiglio poi è quello di usare i BufferedImage i quali hanno un loro oggetto Graphics che ti permette di disegnarci sopra.
Questi sono utili per evitare i flichers dell'immagine.
In più ti permettono di avere un rendering molto ampio della tua scena (precalcolato) per poi farti dare solo un crop dell'immagine che ti serve da sparare sul contenitore su cui visualizzarla.... tecnica del double-buffering!!! ;)

Ah dimenticavo, la teoria sopra è in generale per i componeti che derivvano da JComponent, per quelli AWT basta ridefinire il paint(Graphics g)
valgono le stesse regole per il double buffering ma non per la chiamata al metodo padre anche se non è sconsigliata ;)

Breve esempio
Se hai dubbi chiedi... spero di esserti stato utile.

public class ImageViewer extends JFrame
{
ImapePanel imgPanel = null;

public ImageViewer()
{
super();
setSize(640, 480);

imgPanel = new ImagePanel();
imgPanel.setImage(new ImageIcon("Prova.gif").getImage());
setContentPane(imgPanel);
}
}

class ImagePanel extends JPanel
{
Image imgage = null;

public ImagePanel ()
{
super();
}

public void setImage(Image img)
{
this.image = img;
repaint();
}


protected void paintComponent(Graphics g)
{
// Ci sono casi in cui il componente grafici non è stato ancora creato
if(g == null || image == null) return;

super.paintComponent(g);

g.drawImage(......image);
}
}

fefott
22-09-2006, 08:30
mmmm secondo me, viste le risposte non mi sono spiegata per niente bene...e se le immagini sono 1000 volte meglio delle parole, il codice lo è molto di più dunque...
Nella classe che estende JPanel ho:

public void paintComponent(Graphics g)
{
super.paintComponent(g);
init();

if(game.getStatus() == game.START)
drawStart();

else if (game.getStatus() == game.GAME)
{
drawLabyrinth();
drawProtagonist();
drawAdversary();
drawScore();
drawBonus();
}

else if(game.getStatus() == game.NEXT)
drawNext();

else if (game.getStatus() == game.OVER)
drawOver();

else if (game.getStatus() == game.WIN)
drawWin();

g.drawImage(bufferImage, 0, 0, this);
}

/**
*
*/
public void init()
{
rows = game.maze.getRows();
cols = game.maze.getCols();
sizeCell = setting.sizeCell;
xOff = setting.xOff;
yOff = setting.yOff;

xMaze = cols * sizeCell;
yMaze = rows * sizeCell;

xCanvas = xMaze + xOff*2 ;
yCanvas = yMaze + yOff*2;

// Array di immagini
nImage = setting.nImage;
bonus = new Image[setting.nBonus];
// Carica le immagini
getImages();

bufferImage = createImage(getSize().width, getSize().height);
buffer = bufferImage.getGraphics();
}

------------------------------------------------------------------------

Mentre nella classe JFrame ho:

private void init()
{
setTitle("PacArcade");

// Impedisce il ridimensionamento della frame.
setResizable(false);

// Termina con la chiusura della frame.
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

setting = new Setting();
game = new ControlGame(setting);
panel = new LabyrinthPanel(setting, game);

// Determina le dimensioni della frame.
int xFrame = setting.xOff*3 + game.maze.getCols() * setting.sizeCell;
int yFrame = setting.yOff*3 + game.maze.getRows() * setting.sizeCell + panel.DIM_SCORE*3;

setSize(xFrame, yFrame);

// Centra la finestra sullo schermo.
setLocation((Toolkit.getDefaultToolkit().getScreenSize().width-this.getWidth())/2 ,
(Toolkit.getDefaultToolkit().getScreenSize().height-this.getHeight())/2);

// Dentro ogni frame c'è un container recuperabile con metodo getContentPane().
Container c = getContentPane();
setBackground(BACKGROUND_COLOR);

// I nuovi componenti vanno aggiunti al container.
c.add (panel);
}

-----------------------------------------------
Il labirinto al primo livello è perfetto, viene visualizzato benissimo...lo stesso avviene se faccio finta di partire dal secondo con le dimensioni ingrandite sia del labirinto sia del frame..il problema avviene se parto dal primo livello arrivo alla fine del labirinto e passo al livello successivo..il labirinto si ingrandisce, la frame anche ma il pezzo "aggiunto" non riesco a visualizzarlo.
Spero di essere stata chiara.
Grazie a tutti quelli che verranno in mio soccorso.

MEMon
22-09-2006, 12:23
Prova a stampare le dimensione del bufferImage getSize().width e getSize().height (ti conviene usare getWidth() e getHeight() in quanto non allocano nessun oggetto) e vedi se corrispondono alle dimensioni del JFrame...

Io dico che hai il JPanel più piccolo...rendilo opaco così lo vedi...

fefott
22-09-2006, 13:40
Hai ragione, si ingrandisce il labirinto ma non il JPanel...ma...non mi viene in mente una soluzione...idee??

TellaspallaBob
22-09-2006, 13:41
Uhmmm secondo mo deve esserci qualche inesattezza nei passaggi....

Il codice ovviamente non può essere completo per analizzarlo ma da quello che vedo non mi sembra ci siano errori di sorta.

Anzi prima cosa... non ho potuto vedere l'immagine di cui parli perché il sito su cui l'hai postata richiede l'iscrizione per vederla.... se hai possibilità di inserirla qui è meglio.

Ad occhio penso che sia un problema di tempi, nel senso che non vorrei che tu dimensionassi il buffere image prima che il frame venga dimensionato per il livello successivo.

Nel paint tu gestisci la casistica next e game, spero che in quel next tu non setti la nuova dimensione del frame perchè a quel punto l'init del buffer è già fatto!!!

PS piccolo consiglio, nel paint tu devi solo renderizzare niente logiche di controllo...

fefott
22-09-2006, 13:51
Ho riprovato a rimettere l'immagine, spero si veda..
per quanto riguarda next e game no, non è lì che aumento le dimensioni, sono solo due variabili che mi permettono di cambiare la schermata di passaggio di livello e se è nel gioco la visualizzazione dello stesso. Ho come l'impressione che abbia visto giusto Memon quando dice che secondo lui il JPanel rimane piccolino e non si ingrandisce...ma non so come fare per aggiornare le sue dimensioni (sempre sia quello il problema)..
Suggerimenti??

TellaspallaBob
22-09-2006, 14:02
ok ok!!!

Ora capisco!!!

1 - Il panel lo setti direttamente nel JFrame principale giusto?
Quindi nel tuo JFRame principale avrai una cosa tipo getContentPane().add(panelGioco);

2 - Se si, che LayoutManager hai settato nel ContentPane?
Se leggi il mio primo post, ti dicevo che in java o usi i LayoutManager e quindi devi utilizzare i set e get PreferredSize o lio disabiliti e puoi usare allora i set e get Size....

3 - Se no hai fatto istruzioni tipo getContetPane().setLayou(null) allora di default il ContentPane che è un Conatiner usa un LayoutManeger di tipo FlowLayout...

4 - Soluzioni:
s1 - disabiliti i layout, ma poi il resize te lo devi calcolare tutto a manella.
s2 - non leggi e non setti le dimensioni del pannello mediante set e get size ma mediante set e get PreferredSize che usano oggetti di tipo Dimension

fefott
22-09-2006, 14:13
Allora....come richiamo il tutto l'ho scritto prima...

Mentre nella classe JFrame ho:

private void init()
{
setTitle("PacArcade");

// Impedisce il ridimensionamento della frame.
setResizable(false);

// Termina con la chiusura della frame.
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

setting = new Setting();
game = new ControlGame(setting);
panel = new LabyrinthPanel(setting, game);

// Determina le dimensioni della frame.
int xFrame = setting.xOff*3 + game.maze.getCols() * setting.sizeCell;
int yFrame = setting.yOff*3 + game.maze.getRows() * setting.sizeCell + panel.DIM_SCORE*3;

setSize(xFrame, yFrame);

// Centra la finestra sullo schermo.
setLocation((Toolkit.getDefaultToolkit().getScreenSize().width-this.getWidth())/2 ,
(Toolkit.getDefaultToolkit().getScreenSize().height-this.getHeight())/2);

// Dentro ogni frame c'è un container recuperabile con metodo getContentPane().
Container c = getContentPane();
setBackground(BACKGROUND_COLOR);

// I nuovi componenti vanno aggiunti al container.
c.add (panel);
}

Per quanto riguarda le tue soluzioni...help...ora me le rileggo con calma e provo a vedere come applicarle...
Grazie per l'aiuto che mi stai dando.

TellaspallaBob
22-09-2006, 14:47
Mentre nella classe JFrame ho:

private void init()
{

...........
........

panel = new LabyrinthPanel(setting, game);

.......
........

// Dentro ogni frame c'è un container recuperabile con metodo getContentPane().
Container c = getContentPane();

setBackground(BACKGROUND_COLOR);

// I nuovi componenti vanno aggiunti al container.
c.add (panel);
}



Nel tuo codice non specifici al ContentPane il tipo di Layout usare, quindi lui userà il suo di default cioè il FlowLayout.

soluzione 1 Senza Layoutmanager

class MasterFrame extends JFrame
{
// Riferimento globale al pannello di gioco
PanelGame pnlGame = null;

public MasterFrame()
{
super();

// Disabilito il LayoutManager, ma a questo punto se
// Sei tu che devi gestire uno ad uno tutti i componenti ad esso
// aggiunti.
getContentPane().setLayout(null);

// Creo l'istanza del pannello di gioco
pnlGame = new PanelGame(....parametri....);

// Aggiungo il pannello di gioco al frame.
getContentPane().add(pnlGame);
}


// setto la nuova dimensione del pannello
private void setDimensionePannelloDiGioco(int w, int h)
{
pnlGame.setSize(w,h);

// Ricorda che la variazione di dimensione di un oggetto
// non implica necessariamente il suo ridisegno.
// Se vuoi essere certa fai un bel repaint()
}
}

class PanelGame extends JPanel
{
protected void paintComponent(Graphics g)
{
super.paintComponent(g);

// Visto che le dimensioni sono fisse del pannello
// allora le ricavo con la maniera base
Dimension dimPanel = getSize();
}
}

soluzione 2 CON LayoutManager

class MasterFrame extends JFrame
{
// Riferimento globale al pannello di gioco
PanelGame pnlGame = null;

public MasterFrame()
{
super();

// Disabilito il LayoutManager, ma a questo punto se
// Sei tu che devi gestire uno ad uno tutti i componenti ad esso
// aggiunti.
getContentPane().setLayout(new BorderLayout());

// Creo l'istanza del pannello di gioco
pnlGame = new PanelGame(....parametri....);

// Aggiungo il pannello di gioco al frame.
getContentPane().add(pnlGame);
}

// Questo metodo nel caso di LayoutManage non serve
// perchè la dimensione del pannello è auto-gestita dal LayoutManage
// al variare del Frame Principale
// In essa però puoi vedere come si settano le preferred size
private void setDimensionePannelloDiGioco(int w, int h)
{
// setto le dimensioni preferite al fine di far si che
// il LayoutManager ne tenga conto
pnlGame.setPreferredSize(new Dimension(w,h));
}
}

class PanelGame extends JPanel
{
protected void paintComponent(Graphics g)
{
super.paintComponent(g);

// Visto che le dimensioni sono autogestite dal LayoutManager
// posso ricavarmele mediante le dimensione di preferenza
Dimension dimPanel = getPreferredSize();
}
}

Se la mia ipotesi è giusta, cioè il problema è che stai utilizzando i layoutManager, allora ti consiglio di dare uno sguardo ai tutorial sun per capirli meglio.

Ciao e spero di esseri stato d'aiuto.

fefott
22-09-2006, 18:34
Ho provato tutta la sera le tue soluzioni ma, purtroppo il risultato è sempre lo stesso...striscita nera laterale...poi ho provato a mettere setResizable(true); e giocando a ridimensionare a mano mi sono accorta che una volta che rimpicciolisco e poi ingrandisco il labirinto miracolosamente lui compare per intero...ma di certo questa non è una buona soluzione...

MEMon
22-09-2006, 19:12
Mi è venuto un dubbio, ma il repaint() del jpanel dove disegni, ogni quanto lo chiami? Non è che per caso lo chiami solo una volta?

TellaspallaBob
22-09-2006, 19:15
Ho provato tutta la sera le tue soluzioni ma, purtroppo il risultato è sempre lo stesso...striscita nera laterale...poi ho provato a mettere setResizable(true); e giocando a ridimensionare a mano mi sono accorta che una volta che rimpicciolisco e poi ingrandisco il labirinto miracolosamente lui compare per intero...ma di certo questa non è una buona soluzione...

Allora quello che mi viene in mente potrebbe essere legato a qualche mancato rapaint!!!

Sicurmante sballi qualcosa nella chiamata alla schermata successiva, un passaggio incompleto che per il Thread grafico di Java non significa unnecessario ridisegno della schermata.

Elimina il codice ininfluente, ma posta i macroblocchi del codice fondamentale alla gestione di disegno e gestione logica dei livelli...
mi viene in mente che magari dopo l'ingradimento del frame non fai scattare il repaint.

fefott
23-09-2006, 12:02
Ho modificato alcune cose, ho tappezzato la mia classe di repaint e ho controllato se le variabili interessate venivano aggiornate subito e così ho notato che non ci sono errori ed in effetti se rimpicciolisco il labirinto una volta che l'ho ingrandito riesco a vederlo tutto oppure se metto la frame molto più grande e poi ingrandisco il labirinto riesco a vederlo tutto, se ridimensiono a mano il labirinto lo vedo di nuovo tutto..ma se seguo il gioco e la frame si ingrandisce al superamento del livello vedo la famosa strisciata nera....
Non so più che pensare, sicuramente è una sciocchezza...

TellaspallaBob
23-09-2006, 14:10
Ho modificato alcune cose, ho tappezzato la mia classe di repaint e ho controllato se le variabili interessate venivano aggiornate subito e così ho notato che non ci sono errori ed in effetti se rimpicciolisco il labirinto una volta che l'ho ingrandito riesco a vederlo tutto oppure se metto la frame molto più grande e poi ingrandisco il labirinto riesco a vederlo tutto, se ridimensiono a mano il labirinto lo vedo di nuovo tutto..ma se seguo il gioco e la frame si ingrandisce al superamento del livello vedo la famosa strisciata nera....
Non so più che pensare, sicuramente è una sciocchezza...

Ok questa è l'ultima cosa che mi viene in mente.
Ci sono alcuni casi speciali dove il ridimensionamento o la rimozione e l'aggiunto di componeti a runtime alle volte non viene fatta....
QUesto perché il Thread Grafico non si accorge subito delle modifiche o perchè un listener non lo informa.

Mi è capitato in alcuni casi dove cambiavo il Layout di un pannello a runtime sul quale vi erano disposti alcuni componenti.

Quello che facevo subito dopo aver tolto, aggiunto, spostato i componenti e cambiato il layout era:


invalidate();
// http://java.sun.com/javase/6/docs/api/java/awt/Container.html#invalidate()

doLayout();
// http://java.sun.com/javase/6/docs/api/java/awt/Container.html#doLayout()

ravalidate();
// http://java.sun.com/javase/6/docs/api/javax/swing/JComponent.html#revalidate()

Usa questa serie di istruzioni subito dopo aver chiamato il setSize del tuo Frame Master.

Se nemmeno questo funzica..... posso indicarti uno o due esorcisti !!!
Ciao :D

fefott
23-09-2006, 14:33
SEI UN GENIOOOOOOOOOOOOOOOOOOOO
Ho usato

invalidate();
doLayout();
validate();

(con revalidate non andava)..
e tutto si è risolto...
non so come ringraziarti...dovresti vedere i miei salti di gioia :)