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 ImageIcon
s.
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.
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….
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
}
}
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.
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.
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.
Vous pouvez éviter de lancer complètement votre propre sous-classe Component en utilisant la classe JXImagePanel des bibliothèques free SwingX .
JLabel imgLabel = new JLabel(new ImageIcon("path_to_image.png"));
ImageIcon
s. 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.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); }
}
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.
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 ()).
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();
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"))));
Vous pouvez éviter d’utiliser vos propres bibliothèques Component
s, SwingX et ImageIO
:
File f = new File("hello.jpg");
JLabel imgLabel = new JLabel(new ImageIcon(file.getName()));
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: