Hardware Upgrade Forum

Hardware Upgrade Forum (https://www.hwupgrade.it/forum/index.php)
-   Programmazione (https://www.hwupgrade.it/forum/forumdisplay.php?f=38)
-   -   [Java - Swing] Come applicare una immagine di sfondo ad un JPanel? (https://www.hwupgrade.it/forum/showthread.php?t=2138975)


Player1 06-02-2010 21:47

[Java - Swing] Come applicare una immagine di sfondo ad un JPanel?
 
Ciao a tutti, ho un piccolo problema con l'utilizzo dei file immagine nelle applicazioni java che fanno uso di swing.
Vorrei realizzare una semplice finestra di dimensioni fisse che visualizza una immagine di sfondo e sopra questa immagine alcune icone più piccole posizionate in vari punti di coordinate x,y.
Usando Netbeans ho creato un JFrame di dimensione fissa (uguale a quella dell'immagine di sfondo, una semplice gif).
Dentro il JFrame ho inserito un JPanel vuoto che lo occupa interamente.
Come posso fare a mettere come sfondo del JPanel l'immagine gif?
Ed a posizionare delle altre icone gif all'interno del JPanel, sopra lo sfondo?
Grazie a tutti in anticipo per le risposte.
Ciao.

Player1 07-02-2010 00:58

Posto anche il costruttore del JFrame:
Codice:

public mainFrame() {
                initComponents();
                Graphics g= jPanel1.getGraphics();
                g.drawImage(immagineDiSfondo,0,0,null);
                jPanel1.setVisible(true);
    }

Non riesco a capire dov'è l'errore, appare una finestra grigia.

PGI-Bis 07-02-2010 01:42

Lo stato dell'oggetto "Graphics" restituito dai metodi getGraphics è temporaneo. In un certo senso non è sbagliato dire:

Graphics g= jPanel1.getGraphics();
g.drawImage(immagineDiSfondo,0,0,null);

ma quello che succede è che al primo aggiornamento di jPanel1 "g" viene cancellato e la tua immagine, che pure per un attimo è stata effettivamente disegnata, sparisce. Per rendere "permanente" il tuo disegno devi fare in modo che il:

g.drawImage(immagineDiSfondo,0,0,null);

sia invocato ogni volta che "g" viene aggiornato.

Per farla breve, un modo per garantire questa invocazione è mettere le istruzioni di disegno nel metodo paintComponent del componente AWT/Swing sul cui sfondo vuoi che appaiano i tuoi effetti.

Ti faccio un esempio: qui disegno un quadrato bianco ma puoi benissimo mettere le istruzioni per il disegno di una o più immagini.

Codice:

import java.awt.*;
import javax.swing.*;

public class Main {

        public static void main(String[] args) {
                EventQueue.invokeLater(new Runnable() {
                        public void run() {
                                new Main().startTest();
                        }
                });
        }
       
        private void startTest() {
                JFrame finestra = new JFrame("Finestra");
                JPanel pannello = new JPanel() { //nota che con l'apertura della graffa creiamo una sottoclasse di JPanel
               
                        @Override
                        protected void paintComponent(Graphics graphics) {
                                super.paintComponent(graphics);
                               
                                graphics.setColor(Color.WHITE);
                                graphics.fillRect(0, 0, 100, 100);

                        }
                };
                finestra.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
                finestra.add(pannello);
                finestra.setSize(640, 480);
                finestra.setVisible(true);
        }
}

Il metodo paintComponent viene invocato automaticamente da Swing ogni volta che il framework rileva la necessità di aggiornare il componente. in questo metodo noi abbiamo messo le nostre istruzioni di disegno, quindi queste istruzioni saranno parimenti eseguite ogni volta che il framework dovrà determinare l'aspetto del componente.

Nota che la sovrascrittura non altera il comportamento del componente in quanto contenitore. Significa che puoi continuare a usare "pannello" come un qualsiasi altro JPanel. Se gli aggiungi dei componenti il risultato sarò che il tuo disegno apparirà sotto ai componentei aggiunti (questo per via dell'ordine di invocazione di paintComponent che precede un suo omologo, paintChildren, il quale disegna i componenti contenuti in un contenitore).

Player1 07-02-2010 12:56

Grazie mille, ho provato a fare come dici tu e funziona perfettamente.
Tuttavia vorrei fare una piccola modifica e non ci riesco per via di NetBeans.
In pratica invece di utilizzare questa definizione di pannello
Codice:

JPanel pannello = new JPanel {...}
vorrei creare una classe "extendedJPanel" con tutti i metodi che mi serviranno per gestire al meglio lo sfondo e le icone

Codice:

private class extendedJPanel extends JPanel{
        @Override
        protected void paintComponent(Graphics graphics) {...}

        public int metodo1(){...}
        ...
        public int metodoN(){...}
}

e poi fare
Codice:

extendedJPanel pannello = new extendedJPanel();
Purtroppo utilizzando netbeans non so come includere questo nuovo oggetto di tipo "extendedJPanel" nel JFrame principale poichè nel designer di netbeans ovviamente è presente solo JPanel (e non extendedJPanel) ed il codice relativo alla grafica non può essere modificato a mano (solo trascinando un oggetto dalla palette).
Tu usi NetBeans o Eclipse?
Sai per caso come posso risolvere questo problema ed inserire il mio extendedJPanel nel JFrame principale?
Grazie

PGI-Bis 07-02-2010 14:47

Premetto che i GUI designer (qualsiasi GUI designer) vanno bene per fare interfacce semplici - del genere che forse non varrebbe neanche la pena di fare.

Comunque una soluzione è questa: creare un tuo tipo di componente da aggiungere al pannello dei componenti del gui builder di netbeans.

new project -> java class library -> next
project name -> "PaintablePanel" -> next


new file -> Java class -> class name: PaintablePanel, package "xyz" -> finish

Codice:

package xyz;

import java.awt.Graphics;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JComponent;
import javax.swing.JPanel;

public class PaintablePanel extends JPanel {
    public static interface Painter {

        void paint(Graphics g, JComponent target);
    }
   
    private final List<Painter> LAYERS = new ArrayList<Painter>(5);

    public void addPainter(Painter p) {
        LAYERS.add(p);
    }

    public void removePainter(Painter p) {
        LAYERS.remove(p);
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        for(int i = 0; i < LAYERS.size(); i++) {
            LAYERS.get(i).paint(g, this);
        }
    }
}

Clean and build

Menu "tools" -> Palette -> "Swing/AWT Components"

"New category" -> "Custom Components" -> Ok

Premi "Add from jar"
seleziona il jar "PaintablePanel" che trovi nella cartella "dist" del progetto "PaintablePanel" (es: x:\PaintablePanel\dist\PaintablePanel.jar)
premi next
nella lista trovi "PaintablePanel", selezionalo e premi "next"
Seleziona la categoria "Custom Components" e premi "finish"
Premi close per chiudere il Palette Manager

A questo punto hai il tuo componente nella palette del gui builder, sotto la voce "Custom Components".

Cos'ha di particolare questo PaintablePanel? Direi niente a parte il fatto che ti permette di fare qualcosa che è concettualmente simile al tuo primo tentativo:

Graphics g = pannello.getGraphics();
g.drawqualcosa

cioè ti permette definire una o più procedure di disegno da applicare ad un componente dichiarandole al di fuori della classe del componente.

Per fare una prova, crea un nuovo progetto "test" e aggiungi un JFrame Form . Trascina dalla palette sulla destra "PaintablePanel" nel form.

Premi "Source"

Nel costruttore del form trovi un "initComponents". Dopo questo initComponents scrivi:

Codice:

        Painter painter = new Painter() {

            public void paint(Graphics g, JComponent target) {
                int w = target.getWidth();
                int h = target.getHeight();
                g.setColor(Color.PINK);
                g.fillRect(0, 0, w / 2, h / 2);
            }
        };
        this.paintablePanel1.addPainter(painter);

Se testi il tuo form vedrai che adesso un pezzo del pannello che hai inserito è diventato rosa. Accade perchè PaintablePanel nel suo paintComponent richiama tutti i "Painter" che stanno nella sua lista LAYERS e noi aggiungiamo a questa lista un certo "Painter" che colora di rosa una porzione di quel PaintablePanel.

Player1 07-02-2010 18:47

Fantastico!!!
Fa esattamente quello che volevo come volevo :D
Grazie mille!


Tutti gli orari sono GMT +1. Ora sono le: 10:40.

Powered by vBulletin® Version 3.6.4
Copyright ©2000 - 2021, Jelsoft Enterprises Ltd.
Hardware Upgrade S.r.l.