Ich habe ein JPanel , zu dem ich JPEG- und PNG-Bilder hinzufügen möchte, die ich im laufenden Betrieb generiere.
Alle Beispiele, die ich bisher in den Swing-Tutorials gesehen habe, speziell in den Swing-Beispielen verwenden Sie ImageIcon
s.
Ich generiere diese Bilder als Byte-Arrays und sie sind normalerweise größer als das in den Beispielen verwendete allgemeine Symbol (640 x 480).
Bearbeiten : Eine genauere Betrachtung der Tutorials und der API zeigt, dass Sie ein ImageIcon nicht direkt zu einem JPanel hinzufügen können. Stattdessen erzielen sie den gleichen Effekt, indem sie das Bild als Symbol eines JLabels festlegen. Das fühlt sich einfach nicht richtig an ...
So mache ich das (mit ein wenig mehr Informationen zum Laden eines Bildes):
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
}
}
Wenn Sie JPanels verwenden, arbeiten Sie wahrscheinlich mit Swing. Versuche dies:
BufferedImage myPicture = ImageIO.read(new File("path-to-file"));
JLabel picLabel = new JLabel(new ImageIcon(myPicture));
add(picLabel);
Das Bild ist jetzt eine Swing-Komponente. Es unterliegt Layout-Bedingungen wie jede andere Komponente.
Fred Haslams Weg funktioniert gut. Ich hatte jedoch Probleme mit dem Dateipfad, da ich ein Bild in meinem Glas referenzieren möchte. Dazu habe ich verwendet:
BufferedImage wPic = ImageIO.read(this.getClass().getResource("snow.png"));
JLabel wIcon = new JLabel(new ImageIcon(wPic));
Da ich nur eine begrenzte Anzahl (ungefähr 10) Bilder habe, die ich mit dieser Methode laden muss, funktioniert es ziemlich gut. Es wird eine Datei ohne den richtigen relativen Dateipfad abgerufen.
Ich denke, es gibt keine Notwendigkeit, von irgendetwas eine Unterklasse zu bilden. Verwenden Sie einfach ein Jlabel. Sie können ein Bild in ein Jlabel einfügen. Ändern Sie also die Größe des Jlabels und füllen Sie es mit einem Bild. Es ist in Ordnung. So mache ich das.
JLabel imgLabel = new JLabel(new ImageIcon("path_to_image.png"));
ImageIcon
s verwenden. Für ein einzelnes Bild würde ich darüber nachdenken, eine benutzerdefinierte Unterklasse von JPanel
zu erstellen und die paintComponent
-Methode zu überschreiben, um das Bild zu zeichnen.Sie können JPanel in eine Unterklasse unterteilen - hier ist ein Auszug aus meinem ImagePanel, in dem ein Bild an einer von 5 Positionen platziert wird, oben/links, oben/rechts, Mitte/Mitte, unten/links oder unten/rechts:
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
ist fast immer die falsche Klasse für die Unterklasse. Warum würden Sie JComponent
nicht unterordnen?
Es gibt ein kleines Problem mit ImageIcon
, da der Konstruktor das Lesen des Bildes blockiert. Kein wirkliches Problem beim Laden aus dem Anwendungs-JAR, aber wenn Sie möglicherweise über eine Netzwerkverbindung lesen. Es gibt viele Beispiele aus der AWT-Ära für die Verwendung von MediaTracker
, ImageObserver
und Freunden, sogar in den JDK-Demos.
Ich mache etwas sehr Ähnliches in einem privaten Projekt, an dem ich arbeite. Bisher habe ich problemlos Bilder bis 1024x1024 erzeugt (außer Speicher) und kann sie sehr schnell und ohne Leistungsprobleme anzeigen.
Das Überschreiben der Paint-Methode der JPanel-Unterklasse ist übertrieben und erfordert mehr Arbeit als nötig.
So mache ich es:
Class MapIcon implements Icon {...}
OR
Class MapIcon extends ImageIcon {...}
Der Code, den Sie zum Generieren des Bildes verwenden, gehört zu dieser Klasse. Ich benutze ein BufferedImage um darauf zu zeichnen, wenn paintIcon () aufgerufen wird, benutze g.drawImvge (bufferedImage); Dadurch wird die Anzahl der beim Generieren der Bilder durchgeführten Flash-Vorgänge verringert, und Sie können sie einfädeln.
Als nächstes erweitere ich JLabel:
Class MapLabel extends Scrollable, MouseMotionListener {...}
Dies liegt daran, dass ich mein Bild in einem Bildlauffenster ablegen möchte, d. H. Zeigen Sie einen Teil des Bildes an und lassen Sie den Benutzer nach Bedarf einen Bildlauf durchführen.
Also benutze ich ein JScrollPane, um das MapLabel zu speichern, das nur das MapIcon enthält.
MapIcon map = new MapIcon ();
MapLabel mapLabel = new MapLabel (map);
JScrollPane scrollPane = new JScrollPane();
scrollPane.getViewport ().add (mapLabel);
Aber für dein Szenario (zeige einfach jedes Mal das ganze Bild). Sie müssen das MapLabel zum obersten JPanel hinzufügen und sicherstellen, dass alle auf die volle Größe des Bildes angepasst sind (indem Sie GetPreferredSize () überschreiben).
Erstellen Sie einen Quellordner in Ihrem Projektverzeichnis. In diesem Fall habe ich ihn Bilder genannt.
JFrame snakeFrame = new JFrame();
snakeFrame.setBounds(100, 200, 800, 800);
snakeFrame.setVisible(true);
snakeFrame.add(new JLabel(new ImageIcon("Images/Snake.png")));
snakeFrame.pack();
Diese Antwort ist eine Ergänzung zu @ shawallis Antwort ...
Ich wollte auch ein Bild in meinem Glas referenzieren, aber anstatt ein gepuffertes Bild zu haben, habe ich einfach folgendes gemacht:
JPanel jPanel = new JPanel();
jPanel.add(new JLabel(new ImageIcon(getClass().getClassLoader().getResource("resource/images/polygon.jpg"))));
Sie können die Verwendung der eigenen Component
s- und SwingX-Bibliothek sowie der ImageIO
-Klasse vermeiden:
File f = new File("hello.jpg");
JLabel imgLabel = new JLabel(new ImageIcon(file.getName()));
Ich sehe viele Antworten, die sich nicht wirklich mit den drei Fragen des OP befassen.
1) Ein Wort zur Leistung: Byte-Arrays sind wahrscheinlich nicht effizient, es sei denn, Sie können eine genaue Pixel-Byte-Reihenfolge verwenden, die der aktuellen Auflösung und Farbtiefe Ihrer Grafikkarte entspricht .
Um die beste Zeichenleistung zu erzielen, konvertieren Sie einfach Ihr Bild in ein gepuffertes Bild, das mit einem Typ generiert wird, der Ihrer aktuellen Grafikkonfiguration entspricht. Siehe createCompatibleImage unter https://docs.Oracle.com/javase/tutorial/2d/images/drawonimage.html
Diese Bilder werden nach mehrmaligem Zeichnen ohne Programmieraufwand automatisch im Grafikkartenspeicher zwischengespeichert (dies ist Standard in Swing seit Java 6), sodass das eigentliche Zeichnen vernachlässigbar viel Zeit in Anspruch nimmt - wenn Sie das Bild nicht geändert haben.
Das Ändern des Bildes wird mit einer zusätzlichen Speicherübertragung zwischen Hauptspeicher und GPU-Speicher einhergehen - was langsam ist. Vermeiden Sie es daher, das Bild in ein gepuffertes Bild "neu zu zeichnen". Vermeiden Sie es daher, getPixel und setPixel zu verwenden.
Wenn Sie beispielsweise ein Spiel entwickeln, anstatt alle Spielakteure in ein BufferedImage und dann in ein JPanel zu zeichnen, ist es viel schneller, alle Akteure als kleinere BufferedImages zu laden und einzeln in Ihren JPanel-Code unter zu zeichnen ihre richtige Position - auf diese Weise gibt es keine zusätzliche Datenübertragung zwischen dem Hauptspeicher und dem GPU-Speicher außer der anfänglichen Übertragung der Bilder zum Zwischenspeichern.
ImageIcon verwendet ein BufferedImage unter der Haube - aber im Grunde genommen ist es der Schlüssel, ein BufferedImage mit dem richtigen Grafikmodus zuzuweisen, und es gibt keinen Aufwand, dies richtig zu machen.
2) Üblicherweise wird dazu ein BufferedImage in einer überschriebenen paintComponent-Methode des JPanel gezeichnet. Obwohl Java eine ganze Reihe zusätzlicher Extras unterstützt, wie z. B. Pufferketten, die im GPU-Speicher zwischengespeicherte VolatileImages steuern, ist es nicht erforderlich, diese zu verwenden, da Java 6 einigermaßen hilfreich ist ohne all diese Details der GPU-Beschleunigung offen zu legen.
Beachten Sie, dass die GPU-Beschleunigung bei bestimmten Vorgängen, z. B. beim Strecken durchscheinender Bilder, möglicherweise nicht funktioniert.
3) Nicht hinzufügen. Malen Sie es einfach wie oben erwähnt:
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, this);
}
"Hinzufügen" ist sinnvoll, wenn das Bild Teil des Layouts ist. Wenn Sie dies als Hintergrund- oder Vordergrundbild für das JPanel benötigen, zeichnen Sie einfach in paintComponent. Wenn Sie es vorziehen, eine generische Swing-Komponente zu brauen, die Ihr Bild zeigen kann, dann ist es die gleiche Geschichte (Sie können eine JComponent verwenden und ihre paintComponent-Methode überschreiben) - und fügen Sie dann diese hinzu Ihr Layout der GUI-Komponenten.
4) Konvertieren des Arrays in ein Bufferedimage
Das Konvertieren Ihrer Bytearrays in PNG und das anschließende Laden ist sehr ressourcenintensiv. Eine bessere Möglichkeit besteht darin, das vorhandene Bytearray in ein BufferedImage zu konvertieren.
Dafür: nicht für Schleifen verwenden und Pixel kopieren. Das ist sehr sehr langsam. Stattdessen: