it-swarm.com.de

Wie füge ich ein Bild zu einem JPanel hinzu?

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

Ich generiere diese Bilder als Byte-Arrays und sie sind normalerweise größer als das in den Beispielen verwendete allgemeine Symbol (640 x 480).

  1. Gibt es ein (Leistungs- oder anderes) Problem bei der Verwendung der ImageIcon-Klasse, um ein Bild dieser Größe in einem JPanel anzuzeigen?
  2. Was ist die übliche Vorgehensweise?
  3. Wie kann ich einem JPanel ein Bild hinzufügen, ohne die ImageIcon-Klasse zu verwenden?

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

333
Leonel

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            
    }

}
244
Brendan Cashman

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.

509
Fred Haslam

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.

33
shawalli

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.

32
CoderTim

Mit der JXImagePanel-Klasse aus den free SwingX -Bibliotheken können Sie vermeiden, Ihre eigene Component-Unterklasse vollständig zu rollen.

Download

20
Dan Vinton
JLabel imgLabel = new JLabel(new ImageIcon("path_to_image.png"));
11
user1597002
  1. Es sollte kein Problem geben (abgesehen von allgemeinen Problemen mit sehr großen Bildern).
  2. Wenn Sie über das Hinzufügen mehrerer Bilder zu einem einzelnen Panel sprechen, würde ich ImageIcons 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.
  3. (siehe 2)
8
Michael Myers

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

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

6

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

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"))));
4
Filipe Brito

Sie können die Verwendung der eigenen Components- und SwingX-Bibliothek sowie der ImageIO -Klasse vermeiden:

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

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:

  • lerne die bevorzugte Bytestruktur des BufferedImage (heutzutage ist es sicher, RGB oder RGBA anzunehmen, was 4 Bytes pro Pixel entspricht)
  • lernen Sie die Scanlinie und scannen Sie im Gebrauch (z. B. haben Sie möglicherweise ein 142 Pixel breites Bild - aber im wirklichen Leben wird dieses als 256 Pixel breites Byte-Array gespeichert, da es schneller ist, diese zu verarbeiten und die nicht verwendeten Pixel durch die GPU-Hardware zu maskieren )
  • sobald Sie ein Array nach diesen Grundsätzen erstellt haben, kann die Array-Methode setRGB von BufferedImage Ihr Array in BufferedImage kopieren.
0
Gee Bee