web-dev-qa-db-fra.com

Comment ajouter une image à un JPanel?

J'ai un JPanel auquel j'aimerais ajouter des images JPEG et PNG que je génère à la volée.

Tous les exemples que j'ai vus jusqu'à présent dans Swing Tutorials , spécialement dans les Swing exemples , utilisez ImageIcons.

Je génère ces images sous forme de tableaux d'octets, et elles sont généralement plus grandes que l'icône commune utilisée dans les exemples, à savoir 640x480.

  1. Existe-t-il un problème (de performance ou autre) lors de l'utilisation de la classe ImageIcon pour afficher une image de cette taille dans un fichier JPanel?
  2. Quelle est la manière habituelle de le faire?
  3. Comment ajouter une image à un JPanel sans utiliser la classe ImageIcon?

Edit : un examen plus approfondi des didacticiels et de l'API montre qu'il est impossible d'ajouter un ImageIcon directement à un JPanel. Au lieu de cela, ils obtiennent le même effet en définissant l'image comme une icône d'un JLabel. Cela ne me semble pas juste….

333
Leonel

Voici comment je le fais (avec un peu plus d'informations sur la façon de charger une image):

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            
    }

}
244
Brendan Cashman

Si vous utilisez JPanels, travaillez probablement avec Swing. Essaye ça:

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

L'image est maintenant un composant swing. Il est soumis aux conditions de mise en page comme tout autre composant.

509
Fred Haslam

Le chemin de Fred Haslam fonctionne bien. J'ai eu des problèmes avec le chemin du fichier, car je veux référencer une image dans mon pot. Pour ce faire, j'ai utilisé:

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

Comme je n’ai qu’un nombre fini (environ 10) d’images que j’ai besoin de charger avec cette méthode, cela fonctionne assez bien. Il obtient le fichier sans avoir le chemin de fichier relatif correct.

33
shawalli

Je pense qu'il n'y a pas besoin de sous-classe de rien. Il suffit d'utiliser un Jlabel. Vous pouvez définir une image dans un Jlabel. Alors, redimensionnez le Jlabel puis remplissez-le avec une image. C'est bon. C'est comme ça que je fais.

32
CoderTim

Vous pouvez éviter de lancer complètement votre propre sous-classe Component en utilisant la classe JXImagePanel des bibliothèques free SwingX .

Télécharger

20
Dan Vinton
JLabel imgLabel = new JLabel(new ImageIcon("path_to_image.png"));
11
user1597002
  1. Il ne devrait y avoir aucun problème (autre que les problèmes généraux que vous pourriez avoir avec de très grandes images).
  2. Si vous parlez d’ajouter plusieurs images à un seul panneau, j’utiliserais ImageIcons. Pour une image unique, je penserais à créer une sous-classe personnalisée de JPanel et à remplacer sa méthode paintComponent pour dessiner l'image.
  3. (voir 2)
8
Michael Myers

Vous pouvez sous-classer JPanel - voici un extrait de mon ImagePanel, qui place une image dans l’un des 5 emplacements suivants: haut/gauche, haut/droite, milieu/milieu, bas/gauche ou bas/droite:

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

JPanel est presque toujours la mauvaise classe à sous-classe. Pourquoi ne sous-classez-vous pas JComponent?

Il y a un léger problème avec ImageIcon en ce que le constructeur bloque la lecture de l'image. Ce n’est pas vraiment un problème lors du chargement depuis le fichier JAR de l’application, mais peut-être si vous lisez potentiellement via une connexion réseau. Il existe de nombreux exemples AWT d'utilisation de MediaTracker, ImageObserver et d'amis, même dans les démos JDK.

6

Je fais quelque chose de très similaire dans un projet privé sur lequel je travaille. Jusqu'ici, j'ai généré des images allant jusqu'à 1024x1024 sans aucun problème (sauf la mémoire) et je peux les afficher très rapidement et sans aucun problème de performances.

Remplacer la méthode Paint de la sous-classe JPanel est excessif et nécessite plus de travail que nécessaire.

La façon dont je le fais est:

Class MapIcon implements Icon {...}

OR

Class MapIcon extends ImageIcon {...}

Le code que vous utilisez pour générer l'image sera dans cette classe. J'utilise un BufferedImage pour dessiner dessus lorsque l'appel de paintIcon () est appelé, utilisez g.drawImvge (bufferedImage); Cela réduit la quantité de clignotements effectués lorsque vous générez vos images et vous pouvez les enfiler.

Ensuite, j'étends JLabel:

Class MapLabel extends Scrollable, MouseMotionListener {...}

C'est parce que je veux mettre mon image dans un panneau de défilement, c'est-à-dire affichez une partie de l'image et faites défiler l'utilisateur au besoin.

Alors j'utilise un JScrollPane pour contenir le MapLabel, qui ne contient que le MapIcon.

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

scrollPane.getViewport ().add (mapLabel);

Mais pour votre scénario (montrez simplement l'image entière à chaque fois). Vous devez ajouter le MapLabel au fichier JPanel supérieur et vous assurer de les redimensionner à la taille de l'image (en remplaçant GetPreferredSize ()).

6
Thomas Jones-Low

Créez un dossier source dans votre répertoire de projet, dans ce cas je l'ai appelé Images.

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
user4294909

Cette réponse est un complément à la réponse de @ shawalli ...

Je voulais aussi référencer une image dans mon pot, mais au lieu d'avoir un BufferedImage, j'ai simplement fait ceci:

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

Vous pouvez éviter d’utiliser vos propres bibliothèques Components, SwingX et ImageIO:

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

Je peux voir beaucoup de réponses, ne répondant pas vraiment aux trois questions du PO.

1) Un mot sur les performances: les tableaux d'octets sont probablement inefficaces, sauf si vous pouvez utiliser un ordre exact d'octets en pixels qui correspond à la résolution et à la profondeur de couleur actuelles de vos cartes graphiques. .

Pour obtenir les meilleures performances de dessin, convertissez simplement votre image en une image BufferedImage générée avec un type correspondant à votre configuration graphique actuelle. Voir createCompatibleImage à l'adresse https://docs.Oracle.com/javase/tutorial/2d/images/drawonimage.html

Ces images seront automatiquement mises en cache dans la mémoire de la carte graphique après avoir été dessinées plusieurs fois sans aucun effort de programmation (ceci est la norme dans Swing depuis Java 6). Par conséquent, le dessin proprement dit prendra un temps négligeable - si vous n'avez pas changé l'image.

La modification de l'image entraînera un transfert de mémoire supplémentaire entre la mémoire principale et la mémoire du processeur graphique, ce qui est lent. Evitez donc de "redessiner" l'image dans une BufferedImage, évitez de faire getPixel et setPixel à tout prix.

Par exemple, si vous développez un jeu, au lieu d'attirer tous les acteurs vers une BufferedImage puis vers un JPanel, il est beaucoup plus rapide de charger tous les acteurs en tant que petits BufferedImages et de les dessiner un par un dans votre code JPanel à l'adresse leur position correcte - de cette manière, il n’ya pas de transfert de données supplémentaire entre la mémoire principale et la mémoire du processeur graphique, à l’exception du transfert initial des images à mettre en cache.

ImageIcon utilisera une image BufferedImage sous le capot - mais l’allocation d’une image BufferedImage avec le mode graphique approprié est la clé, et aucun effort n’est fait pour le faire correctement.

2) La méthode habituelle consiste à dessiner une image BufferedImage dans une méthode paintComponent surchargée de JPanel. Bien que Java prenne en charge un bon nombre d’ajouts supplémentaires, tels que des chaînes de tampons contrôlant VolatileImages en cache dans la mémoire du GPU, il n’est pas nécessaire de les utiliser, car Java 6 est relativement bon. travail sans exposer tous ces détails de l’accélération GPU.

Notez que l'accélération du processeur graphique peut ne pas fonctionner avec certaines opérations, telles que l'étirement d'images translucides.

3) N'ajoutez pas. Il suffit de peindre comme mentionné ci-dessus:

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

"Ajouter" est logique si l'image fait partie de la mise en page. Si vous avez besoin de cela comme image d’arrière-plan ou d’avant-plan pour remplir JPanel, dessinez simplement paintComponent. Si vous préférez brasser un composant Swing générique pouvant afficher votre image, il s'agit alors de la même histoire (vous pouvez utiliser un composant JComponent et remplacer sa méthode paintComponent) - puis ajoutez this à votre disposition des composants de l'interface graphique.

4) Comment convertir le tableau en image tamponnée

Convertir vos tableaux d'octets au format PNG, puis le charger consomme beaucoup de ressources. Un meilleur moyen consiste à convertir votre tableau d'octets existant en image BufferedImage.

Pour cela: n'utilisez pas de boucles for et copiez des pixels. C'est très très lent. Au lieu:

  • apprendre la structure en octets préférée de BufferedImage (de nos jours, il est prudent d'assumer RGB ou RGBA, ce qui correspond à 4 octets par pixel)
  • apprendre la ligne de numérisation et la taille de numérisation en cours d'utilisation (par exemple, vous pourriez avoir une image large de 142 pixels - mais dans la réalité, elle sera stockée sous la forme d'un tableau d'octets d'une largeur de 256 pixels car il est plus rapide de traiter cela et de masquer les pixels inutilisés par le matériel du processeur graphique )
  • une fois que vous avez créé un tableau conformément à ces principes, la méthode setRGB array de BufferedImage peut copier votre tableau sur BufferedImage.
0
Gee Bee