Come aggiungere un'immagine a JPanel?


341

Ho un JPanel al quale vorrei aggiungere immagini JPEG e PNG che ho generato al volo.

Tutti gli esempi che ho visto finora negli Swing Tutorials , specialmente negli esempi Swing, usano ImageIcons.

Sto generando queste immagini come array di byte, e di solito sono più grandi dell'icona comune che usano negli esempi, a 640x480.

  1. C'è qualche problema (prestazioni o altro) nell'uso della classe ImageIcon per visualizzare un'immagine di quelle dimensioni in un JPanel?
  2. Qual è il solito modo di farlo?
  3. Come aggiungere un'immagine a JPanel senza usare la classe ImageIcon?

Modifica : un esame più attento dei tutorial e dell'API mostra che non è possibile aggiungere un ImageIcon direttamente a un JPanel. Al contrario, ottengono lo stesso effetto impostando l'immagine come icona di una JLabel. Questo non sembra giusto ...


1
A seconda di come si stanno generando gli array di byte, potrebbe essere più efficiente utilizzare a MemoryImageSourcepiuttosto che convertirli in formato JPEG o PNG e quindi leggere ImageIOcome suggerisce la maggior parte delle risposte. È possibile ottenere un Imageda un MemoryImageSourcecostruito con i dati delle immagini utilizzando createImagee visualizzare come suggerito in una delle risposte.
Alden,

Risposte:


252

Ecco come lo faccio (con un po 'più di informazioni su come caricare un'immagine):

import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JPanel;

public class ImagePanel extends JPanel{

    private BufferedImage image;

    public ImagePanel() {
       try {                
          image = ImageIO.read(new File("image name and path"));
       } catch (IOException ex) {
            // handle exception...
       }
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.drawImage(image, 0, 0, this); // see javadoc for more info on the parameters            
    }

}

17
-1 per l'implementazione non valida di paintComponent (@Dogmatixed molto probabilmente è per questo che stai riscontrando quei problemi di ridisegno) - deve garantire di coprire la sua area completa se segnala di essere opaco (che è l'impostazione predefinita), più facile da ottenere chiamando super.paintComponent
Kleopatra,

5
@kleopatra, grazie, non mi ero reso conto che ... secondo javadoc: "Inoltre, se non invochi l'implementazione di super devi onorare la proprietà opaca, cioè se questo componente è opaco, devi compilare completamente il sfondo in un colore non opaco. Se non onori la proprietà opaca, vedrai probabilmente artefatti visivi. " Aggiornerò la risposta ora.
Brendan Cashman,

8
Si prega di rispettare sempre i Principle of Encapsulationmetodi paintComponent(...)protectedpublic
prioritari

1
Questo non va bene, non fa nulla per il dimensionamento del pannello all'immagine. Più codice dovrà essere aggiunto in seguito per occuparsi di questo. Molto più semplice usare la risposta JLabel.
Keilly,

2
@Jonathan: mi dispiace davvero, per quanto riguarda alcune fonti per Principle of Encapsulation. Ricorda solo che durante la programmazione, ciò che dovrebbe essere nascosto dal resto del codice, deve essere mantenuto in questo modo, invece di essere visibile a tutto ciò che è nel codice. Sembra che sia una cosa del genere che viene dall'esperienza, come si impara ogni giorno. Ogni libro può dare un'idea di base di cosa sia l'incapsulamento, ma è il modo in cui implementiamo il nostro codice che decide quanto aderiamo al concetto.
nIcO circa

520

Se stai usando JPanels, probabilmente stai lavorando con Swing. Prova questo:

BufferedImage myPicture = ImageIO.read(new File("path-to-file"));
JLabel picLabel = new JLabel(new ImageIcon(myPicture));
add(picLabel);

L'immagine è ora un componente swing. Diventa soggetto a condizioni di layout come qualsiasi altro componente.


16
come ridimensionare l'immagine in base alle dimensioni di JLabel?
coding_idiot

1
Bel codice! Non ho molta esperienza con Swing ma non riesco a farlo funzionare. Qualcuno l'ha provato in jdk 1.6.0_16?
ATorras,

3
@ATorras So che l'hai chiesto qualche tempo fa, ma se qualche altro neofita aveva i miei problemi, ricordati di picLabel.setBounds ();
Kyle,

Devo creare un nuovo oggetto JLabel ogni volta che viene ricaricata una foto?
0x6B6F77616C74

2
@coding_idiot new JLabel (nuovo ImageIcon (backgroundImage.getScaledInstance (larghezza, altezza, Image.SCALE_FAST)));
Davide

37

Il modo di Fred Haslam funziona bene. Ho avuto problemi con il percorso del file, dal momento che voglio fare riferimento a un'immagine nel mio vaso. Per fare questo, ho usato:

BufferedImage wPic = ImageIO.read(this.getClass().getResource("snow.png"));
JLabel wIcon = new JLabel(new ImageIcon(wPic));

Dato che ho solo un numero finito (circa 10) di immagini che devo caricare usando questo metodo, funziona abbastanza bene. Ottiene il file senza dover disporre del percorso file relativo corretto.


1
Ho sostituito la prima riga con BufferedImage previewImage = ImageIO.read(new URL(imageURL));per ottenere le mie immagini da un server.
creatore

Per riferendosi immagini dal vaso, provate questo stackoverflow.com/a/44844922/3211801
Nandha

33

Penso che non sia necessario sottoclassare nulla. Usa solo un Jlabel. È possibile impostare un'immagine in un Jlabel. Quindi, ridimensiona il Jlabel quindi riempilo con un'immagine. Va bene. Questo è il modo in cui lo faccio.


20

Puoi evitare di arrotolare completamente la tua sottoclasse Componente usando la classe JXImagePanel dalle librerie SwingX gratuite .

Scarica


11
JLabel imgLabel = new JLabel(new ImageIcon("path_to_image.png"));

8

Puoi sottoclassare JPanel - ecco un estratto dal mio ImagePanel, che mette un'immagine in una delle 5 posizioni, in alto / a sinistra, in alto / a destra, in mezzo / in mezzo, in basso / a sinistra o in basso / a destra:

protected void paintComponent(Graphics gc) {
    super.paintComponent(gc);

    Dimension                           cs=getSize();                           // component size

    gc=gc.create();
    gc.clipRect(insets.left,insets.top,(cs.width-insets.left-insets.right),(cs.height-insets.top-insets.bottom));
    if(mmImage!=null) { gc.drawImage(mmImage,(((cs.width-mmSize.width)/2)       +mmHrzShift),(((cs.height-mmSize.height)/2)        +mmVrtShift),null); }
    if(tlImage!=null) { gc.drawImage(tlImage,(insets.left                       +tlHrzShift),(insets.top                           +tlVrtShift),null); }
    if(trImage!=null) { gc.drawImage(trImage,(cs.width-insets.right-trSize.width+trHrzShift),(insets.top                           +trVrtShift),null); }
    if(blImage!=null) { gc.drawImage(blImage,(insets.left                       +blHrzShift),(cs.height-insets.bottom-blSize.height+blVrtShift),null); }
    if(brImage!=null) { gc.drawImage(brImage,(cs.width-insets.right-brSize.width+brHrzShift),(cs.height-insets.bottom-brSize.height+brVrtShift),null); }
    }

8
  1. Non dovrebbero esserci problemi (diversi da quelli generali che potresti avere con immagini molto grandi).
  2. Se stai parlando di aggiungere più immagini a un singolo pannello, userei ImageIcons. Per una singola immagine, penserei di creare una sottoclasse personalizzata JPanele di sovrascriverne il paintComponentmetodo per disegnare l'immagine.
  3. (vedi 2)

6

JPanelè quasi sempre la classe sbagliata da sottoclasse. Perché non dovresti sottoclassareJComponent ?

C'è un leggero problema ImageIconin quanto il costruttore blocca la lettura dell'immagine. Non è davvero un problema quando si carica dal vaso dell'applicazione, ma forse se si sta potenzialmente leggendo su una connessione di rete. C'è un sacco di esempi AWT-era di usare MediaTracker, ImageObservere amici, anche nelle demo JDK.


6

Sto facendo qualcosa di molto simile in un progetto privato a cui sto lavorando. Finora ho generato immagini fino a 1024x1024 senza problemi (tranne la memoria) e posso visualizzarle molto rapidamente e senza problemi di prestazioni.

Sostituire il metodo di pittura della sottoclasse JPanel è eccessivo e richiede più lavoro di quanto sia necessario.

Il modo in cui lo faccio è:

Class MapIcon implements Icon {...}

O

Class MapIcon extends ImageIcon {...}

Il codice che usi per generare l'immagine sarà in questa classe. Uso un BufferedImage per disegnare su quando poi viene chiamato paintIcon (), uso g.drawImvge (bufferedImage); Ciò riduce la quantità di lampeggi eseguiti mentre si generano le immagini e si può thread.

Quindi estendo JLabel:

Class MapLabel extends Scrollable, MouseMotionListener {...}

Questo perché voglio mettere la mia immagine in un riquadro di scorrimento, ovvero visualizzare parte dell'immagine e far scorrere l'utente secondo necessità.

Quindi uso un JScrollPane per contenere MapLabel, che contiene solo MapIcon.

MapIcon map = new MapIcon (); 
MapLabel mapLabel = new MapLabel (map);
JScrollPane scrollPane = new JScrollPane();

scrollPane.getViewport ().add (mapLabel);

Ma per il tuo scenario (mostra solo l'intera immagine ogni volta). Devi aggiungere MapLabel alla parte superiore di JPanel e assicurarti di ridimensionarli tutti a dimensione intera dell'immagine (sovrascrivendo GetPreferredSize ()).


5

Crea una cartella di origine nella directory del tuo progetto, in questo caso l'ho chiamata Immagini.

JFrame snakeFrame = new JFrame();
snakeFrame.setBounds(100, 200, 800, 800);
snakeFrame.setVisible(true);
snakeFrame.add(new JLabel(new ImageIcon("Images/Snake.png")));
snakeFrame.pack();

5

Puoi evitare di usare la propria Componentlibreria e ImageIOclasse SwingX e se :

File f = new File("hello.jpg");
JLabel imgLabel = new JLabel(new ImageIcon(file.getName()));

4

Questa risposta è un complemento alla risposta di @ shawalli ...

Volevo fare riferimento anche a un'immagine all'interno del mio vaso, ma invece di avere un BufferedImage, ho semplicemente fatto questo:

 JPanel jPanel = new JPanel();      
 jPanel.add(new JLabel(new ImageIcon(getClass().getClassLoader().getResource("resource/images/polygon.jpg"))));

1

Riesco a vedere molte risposte, non rispondendo realmente alle tre domande del PO.

1) Una parola sulle prestazioni: gli array di byte sono probabilmente inefficienti a meno che non sia possibile utilizzare un esatto ordinamento di byte di pixel che corrisponda alla risoluzione corrente e alla profondità del colore degli adattatori del display.

Per ottenere le migliori prestazioni di disegno, è sufficiente convertire l'immagine in un'immagine bufferizzata che viene generata con un tipo corrispondente alla configurazione grafica corrente. Vedi createCompatibleImage su https://docs.oracle.com/javase/tutorial/2d/images/drawonimage.html

Queste immagini verranno automaticamente memorizzate nella memoria della scheda video dopo essere state disegnate alcune volte senza alcuno sforzo di programmazione (questo è standard in Swing da Java 6), e quindi il disegno effettivo richiederà un tempo trascurabile, se non si modifica l'immagine .

La modifica dell'immagine comporterà un ulteriore trasferimento di memoria tra la memoria principale e la memoria GPU, che è lento. Evita quindi di "ridisegnare" l'immagine in un'immagine bufferizzata, quindi evita di ottenere getPixel e setPixel in alcun modo.

Ad esempio, se stai sviluppando un gioco, invece di attirare tutti gli attori del gioco su un'immagine bufferizzata e quindi su JPanel, è molto più veloce caricare tutti gli attori come immagini bufferizzate più piccole e disegnarli uno a uno nel codice JPanel in la loro posizione corretta - in questo modo non vi è alcun trasferimento di dati aggiuntivo tra la memoria principale e la memoria GPU tranne il trasferimento iniziale delle immagini per la memorizzazione nella cache.

ImageIcon utilizzerà una BufferedImage sotto il cofano, ma fondamentalmente l'allocazione di una BufferedImage con la corretta modalità grafica è la chiave, e non c'è nessuno sforzo per farlo bene.

2) Il solito modo di fare questo è di disegnare un'immagine tamponata con un metodo di vernice sovrascritta Componente di JPanel. Sebbene Java supporti una buona quantità di extra aggiuntivi come catene di buffer che controllano VolatileImages memorizzati nella cache della memoria GPU, non è necessario utilizzare nessuno di questi dal momento che Java 6 fa un lavoro ragionevolmente buono senza esporre tutti questi dettagli dell'accelerazione GPU.

Si noti che l'accelerazione GPU potrebbe non funzionare per alcune operazioni, come lo stiramento di immagini traslucide.

3) Non aggiungere. Dipingi semplicemente come indicato sopra:

@Override
protected void paintComponent(Graphics g) {
    super.paintComponent(g);
    g.drawImage(image, 0, 0, this); 
}

"Aggiunta" ha senso se l'immagine fa parte del layout. Se hai bisogno di questo come immagine di sfondo o di primo piano che riempie JPanel, disegna semplicemente paintComponent. Se si preferisce birra una componente Swing generica che può mostrare la vostra immagine, allora è la stessa storia (si può utilizzare un JComponent e ignorare il suo metodo paintComponent) - e quindi aggiungere questo al layout dei componenti GUI.

4) Come convertire l'array in un'immagine bufferizzata

La conversione delle matrici di byte in PNG, quindi il caricamento richiede molta risorse. Un modo migliore è convertire l'array di byte esistente in una BufferedImage.

Per questo: non utilizzare per loop e copiare pixel. È molto lento. Anziché:

  • apprendere la struttura di byte preferita di BufferedImage (al giorno d'oggi è sicuro assumere RGB o RGBA, che è di 4 byte per pixel)
  • apprendi la linea di scansione e la scansione in uso (ad esempio potresti avere un'immagine larga 142 pixel - ma nella vita reale che verrà memorizzata come matrice di byte larga 256 pixel poiché è più veloce elaborarla e mascherare i pixel non utilizzati dall'hardware GPU )
  • quindi, una volta creato un array secondo questi principi, il metodo array setRGB di BufferedImage può copiare l'array su BufferedImage.
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.