Domanda

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

Tutti gli esempi che ho visto finora nei Swing Tutorials , specialmente negli Esempi di oscillazione utilizzare ImageIcon s.

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 ...

È stato utile?

Soluzione

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            
    }

}

Altri suggerimenti

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.

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) immagini che devo caricare usando questo metodo, funziona abbastanza bene. Ottiene il file senza dover avere il percorso file relativo corretto.

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.

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

Scarica

JLabel imgLabel = new JLabel(new ImageIcon("path_to_image.png"));
  1. Non dovrebbero esserci problemi (a parte qualsiasi problema generale che potresti avere con immagini molto grandi).
  2. Se stai parlando di aggiungere più immagini a un singolo pannello, userei ImageIcon . Per una singola immagine, vorrei pensare di creare una sottoclasse personalizzata di JPanel e di sovrascrivere il suo metodo paintComponent per disegnare l'immagine.
  3. (vedi 2)

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); }
    }

JPanel è quasi sempre la classe sbagliata da sottoclassare. Perché non dovresti sottoclassare JComponent ?

Vi è un leggero problema con ImageIcon in 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. Ci sono molti esempi dell'era AWT dell'uso di MediaTracker , ImageObserver e degli amici, anche nelle demo di JDK.

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.

L'override del metodo di disegno 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ò eseguire il thread.

Successivamente estendo JLabel:

Class MapLabel extends Scrollable, MouseMotionListener {...}

Questo perché voglio mettere la mia immagine in un riquadro di scorrimento, ad es. 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 sempre 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 ()).

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();

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"))));

Puoi evitare di usare la propria libreria Component e SwingX e la classe ImageIO :

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

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 ordinamento esatto di byte di pixel che corrisponda al display adattando la risoluzione corrente e l'intensità del colore.

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à una quantità trascurabile di tempo - se non hai cambiato l'immagine.

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

Ad esempio, se stai sviluppando un gioco, invece di disegnare tutti gli attori del gioco su un'immagine bufferizzata e poi su JPanel, è molto più veloce caricare tutti gli attori come immagini bufferizzate più piccole e disegnarli uno per uno nel tuo Codice JPanel nella posizione corretta: in questo modo non è possibile trasferire dati aggiuntivi tra la memoria principale e la memoria GPU ad eccezione del 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 modalità grafica corretta è la chiave, e non c'è nessuno sforzo per farlo bene.

2) Il modo abituale per farlo è disegnare un'immagine con buffer in un metodo di PaintComponent ignorato 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.

Tieni presente che l'accelerazione GPU potrebbe non funzionare per determinate 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 preferisci preparare un componente Swing generico che può mostrare la tua immagine, allora è la stessa storia (puoi usare un JComponent e sovrascriverne il metodo paintComponent) - e quindi aggiungere questo al layout dei componenti della GUI .

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

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

Per questo: non usare per loop e copiare pixel. È molto lento. Invece:

  • impara la struttura di byte preferita di BufferedImage (al giorno d'oggi è sicuro assumere RGB o RGBA, che è di 4 byte per pixel)
  • impara 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 da l'hardware della GPU)
  • quindi una volta che hai una build di array secondo questi principi, il metodo di array setRGB di BufferedImage può copiare il tuo array su BufferedImage.
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top