Bild in JPanel setzen

yachty66

Mitglied
In der Klasse "setImage" will wird ein Bild im Konstruktor empfangen (das Bild kommt über einen JFileChooser) dieses Bild möchte ich jetzt in meinem JPanel setzen (also anzeigen lassen), allerdings weiß ich nicht wie ich vorgehen soll. Hat jemand eine Lösung?

Java:
public class ImagePanel extends JPanel {


    public ImagePanel() {
        setSize(31, 78);
            
    }

    public void setImage(LFAImage image) {
        
    }
 
das Bild kommt über einen JFileChooser) dieses Bild möchte ich jetzt in meinem JPanel setzen (also anzeigen lassen), allerdings weiß ich nicht wie ich vorgehen soll. Hat jemand eine Lösung?
Hier ein Beispiel mit einem herkömmlichen Image.
1626909289441.png

[CODE lang="java" title="Start"]import javax.swing.JFrame;

public class Start {
public static void main(String[] args) {
JFrame frame = new JFrame("Image Load");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new ImageSetPanel(600, 600));
frame.pack();
frame.setResizable(false);
frame.setVisible(true);
}
}[/CODE]
[CODE lang="java" title="ImagePanel" highlight="11,12,30-33,37-49"]import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import javax.swing.BorderFactory;
import javax.swing.JPanel;

@SuppressWarnings("serial")
public class ImagePanel extends JPanel {
private Image img;
private Point pos = new Point();

public ImagePanel(int width, int height) {
setPreferredSize(new Dimension(width, height));
setBackground(Color.DARK_GRAY);
setBorder(BorderFactory.createLineBorder(Color.WHITE, 2));
}

public void centerImage(int width, int height) {
pos.x = 0;
pos.y = 0;
if (width < getWidth())
pos.x = (getWidth() - width) / 2;
if (height < getHeight())
pos.y = (getHeight() - height) / 2;
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null)
g.drawImage(img, pos.x, pos.y, this);
}

public void setImage(Image img) {
if (img == null)
return;
int width = getWidth();
int height = getHeight();
int imgW = img.getWidth(this);
int imgH = img.getHeight(this);
float rel = imgW / (float) imgH;
if (imgW < imgH)
width = (int) (height * rel);
else
height = (int) (width / rel);
this.img = img.getScaledInstance(width, height, Image.SCALE_SMOOTH);
centerImage(width, height);
}

}[/CODE]
[CODE lang="java" title="ImageSetPanel"]import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Image;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.filechooser.FileNameExtensionFilter;

@SuppressWarnings("serial")
public class ImageSetPanel extends JPanel {
JFileChooser chooser = new JFileChooser();

private JButton open = new JButton("load Image");
private ImagePanel image;

public ImageSetPanel(int width, int height) {
setPreferredSize(new Dimension(width, height));
setLayout(new BorderLayout(5, 5));
image = new ImagePanel(height, (int) (height * .9));
add(open, BorderLayout.NORTH);
add(image, BorderLayout.CENTER);
chooser.setFileFilter(new FileNameExtensionFilter("JPG & GIF Images", "jpg", "gif"));
open.addActionListener(e -> {
int ok = chooser.showOpenDialog(this);
if (ok == JFileChooser.APPROVE_OPTION) {
Image img;
try {
img = ImageIO.read(chooser.getSelectedFile());
} catch (IOException e1) {
JOptionPane.showMessageDialog(this, "Can't load " + chooser.getSelectedFile().getName());
return;
}
image.setImage(img);
repaint();
}
});
}
}[/CODE]
😉
 
Guten Tag @Blender3D
Erst einmal vielen Dank! Ist ein gutes Lehrstück!

sorry, wenn ich den alten Thread nochmals anspreche.

int width = getWidth(); ist immer 0
Code:
public void setImage(Image img) {
    if (img == null)
        return;
    int width = getWidth();
    int height = getHeight();
    int imgW = img.getWidth(this);
    int imgH = img.getHeight(this);
    float rel = imgW / (float) imgH;
    if (imgW < imgH)
        width = (int) (height * rel);
    else
        height = (int) (width / rel);
    this.img = img.getScaledInstance(width, height, Image.SCALE_SMOOTH);
    centerImage(width, height);
}
Instanziiert wird es aber mit:
setPreferredSize(new Dimension(width, height));
sollte es dann nicht:
Java:
int width = getPreferredSize().width;
int height = getPreferredSize().height;
heissen? (Dann bekomme ich eine Grösse zurück)
Oder habe ich iwo anders einen Fehler gemacht? (Copy/Past)
😳

PS:
Ich glaub ich hab wieder was gelernt:
Das ganze ist noch nicht visible!
Darum muss ich in der Klasse: ImagePanel() alle
getHeight() durch > getPreferredSize().height und
getWidth() durch > getPreferredSize().width
ersetzen.
 
Zuletzt bearbeitet:
Das ganze ist noch nicht visible!
Ja, die Komponente erhält erst ihre Breite/Höhe, wenn sie "realisiert" wird. Grund ist das Layout-Management, das dafür sorgt, dass sich die Größen der Komponenten automatisch am Bildschirm anpassen.

Wenn etwas am Bildschirm angezeigt wird, steht nur begrenzt Platz zur Verfügung und die Frage ist, wie dieser Platz verteilt wird.

Der erste Part besteht nun darin, für den Fensterinhalt die "gewünschte" Größe (preferred size) zu ermitteln. Wenn diese nicht explizit gesetzt wurde, wird die preferred Size der einzelnen Komponenten abgerufen und gemäß dem verwendeten Layout verteilt, um so die gewünschte Größe des Containers zu erhalten.

Rufst Du JFrame#pack() auf, wird versucht, die Größe des Fensters so festzulegen, dass alle Komponenten mit ihrer preferred Size dargestellt werden können. Passt das Fenster nicht auf den Bildschirm, oder legst Du eine geringere Größe fest, funktioniert das natürlich nicht mehr.

Jetzt kommt der zweite Part: die tatsächliche Position und Größe der Komponenten wird - entsprechend dem Layout - festgelegt. Hier wird dann setBounds() aufgerufen und erst dann hat die Komponte tatsächlich ihr Größe erhalten (getWidth(), getHeight() liefert dann einen vernünftigen Wert).

Das ist also ein Wechselspiel zwischen "so groß würde ich es gerne haben" und "es muss aber so groß sein".
 
Guten Tag @Blender3D
Erst einmal vielen Dank! Ist ein gutes Lehrstück!

sorry, wenn ich den alten Thread nochmals anspreche.
Sorry wofür? Danke dafür, dass du dich mit meinen Code befasst und für dein Feedback. 🙂

Instanziiert wird es aber mit:
sollte es dann nicht:
Java:
int width = getPreferredSize().width;
int height = getPreferredSize().height;
heissen? (Dann bekomme ich eine Grösse zurück)
Oder habe ich iwo anders einen Fehler gemacht? (Copy/Past)
😳

PS:
Ich glaub ich hab wieder was gelernt:
Das ganze ist noch nicht visible!
Darum muss ich in der Klasse: ImagePanel() alle
getHeight() durch > getPreferredSize().height und
getWidth() durch > getPreferredSize().width
ersetzen.
Ja und nein. Das hätte einen unerwünschten Nebeneffekt. Die Idee der Klasse ImagePanel ist, die zur Verfügung stehende Größe so zu nutzen, dass auf jeden Fall das ganze Bild angezeigt wird. Das wäre mit deinem Vorschlag aber nicht sicher gestellt falls der Platz für die gewünschte Größe nicht ausreicht.
Aber im meinem Code sind bei der Durchsicht neben dem Fehler auf den du hingewiesen hast. ( Ein Benutzer setzt ein Bild ein bevor der Layoutmanager die konkrete Größe ermittelt hat und das Bild wird nicht eingesetzt ) auch noch andere Fehler aufgetaucht.
Die Hilfsmethode centerImage sollte nicht public sein. Und der Fall dass das Bildformat quadratisch ist wird in dem Code auch nicht behandelt.
Hier der nachgebesserte Code.
Java:
import javax.swing.JFrame;

public class Start {
    public static void main(String[] args) {
        JFrame frame = new JFrame("Image Load");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new ImageSetPanel(600, 600));
        frame.pack();
        frame.setResizable(false);
        frame.setVisible(true);
    }
}
Java:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import javax.swing.BorderFactory;
import javax.swing.JPanel;

@SuppressWarnings("serial")
public class ImagePanel extends JPanel {
    private Image img;
    private Point pos = new Point();

    public ImagePanel(int width, int height) {
        setPreferredSize(new Dimension(width, height));
        setBackground(Color.DARK_GRAY);
        setBorder(BorderFactory.createLineBorder(Color.WHITE, 2));
    }

    private void centerImage(int width, int height) {
        pos.x = 0;
        pos.y = 0;
        if (width < getWidth())
            pos.x = (getWidth() - width) / 2;
        if (height < getHeight())
            pos.y = (getHeight() - height) / 2;
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (img != null)
            g.drawImage(img, pos.x, pos.y, this);
    }

    /**
     * Sets an image to be displayed. The image automatically adjusts to the
     * available size. Important: Call this method only after the layout manager has
     * calculated the available space. Otherwise, the specific available size isn't
     * yet available, and the image won't be set.
     *
     * @param img Image to be set.
     */
    public void setImage(Image img) {
        int width = getWidth();
        int height = getHeight();
        if (img == null || width == 0 || height == 0)
            return;
        int imgW = img.getWidth(this);
        int imgH = img.getHeight(this);
        float rel = imgW / (float) imgH;
        if (imgH != imgW) {
            if (imgW < imgH)
                width = (int) (height * rel);
            else
                height = (int) (width / rel);
        } else {
            if (width < height)
                height = width;
            else
                width = height;
        }
        this.img = img.getScaledInstance(width, height, Image.SCALE_SMOOTH);
        centerImage(width, height);
    }

}
Java:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Image;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.filechooser.FileNameExtensionFilter;

@SuppressWarnings("serial")
public class ImageSetPanel extends JPanel {
    private JFileChooser chooser = new JFileChooser();
    private JButton open = new JButton("load Image");
    private ImagePanel imgPanel;

    public ImageSetPanel(int width, int height) {
        setPreferredSize(new Dimension(width, height));
        setLayout(new BorderLayout(5, 5));
        imgPanel = new ImagePanel(height, (int) (height * .9));
        add(open, BorderLayout.NORTH);
        add(imgPanel, BorderLayout.CENTER);
        chooser.setFileFilter(new FileNameExtensionFilter("JPG & GIF Images", "jpg", "gif"));
        open.addActionListener(e -> {
            int ok = chooser.showOpenDialog(this);
            if (ok == JFileChooser.APPROVE_OPTION) {
                Image img;
                try {
                    img = ImageIO.read(chooser.getSelectedFile());
                } catch (IOException e1) {
                    JOptionPane.showMessageDialog(this, "Can't load " + chooser.getSelectedFile().getName());
                    return;
                }
                imgPanel.setImage(img);
                repaint();
            }
        });
    }
}
 

Anhänge

Guten Tag @Blender3D
Erst einmal vielen Dank! Ist ein gutes Lehrstück!

sorry, wenn ich den alten Thread nochmals anspreche.
Sorry wofür? Danke dafür, dass du dich mit meinen Code befasst und für dein Feedback. 🙂

Instanziiert wird es aber mit:
sollte es dann nicht:
Java:
int width = getPreferredSize().width;
int height = getPreferredSize().height;
heissen? (Dann bekomme ich eine Grösse zurück)
Oder habe ich iwo anders einen Fehler gemacht? (Copy/Past)
😳

PS:
Ich glaub ich hab wieder was gelernt:
Das ganze ist noch nicht visible!
Darum muss ich in der Klasse: ImagePanel() alle
getHeight() durch > getPreferredSize().height und
getWidth() durch > getPreferredSize().width
ersetzen.
Ja und nein. Das hätte einen unerwünschten Nebeneffekt. Die Idee der Klasse ImagePanel ist, die zur Verfügung stehende Größe so zu nutzen, dass auf jeden Fall das ganze Bild angezeigt wird. Das wäre mit deinem Vorschlag aber nicht sicher gestellt falls der Platz für die gewünschte Größe nicht ausreicht.
Aber im meinem Code sind bei der Durchsicht neben dem Fehler auf den du hingewiesen hast. ( Ein Benutzer setzt ein Bild ein bevor der Layoutmanager die konkrete Größe ermittelt hat und das Bild wird nicht eingesetzt ) auch noch andere Fehler aufgetaucht.
Die Hilfsmethode centerImage sollte nicht public sein. Und der Fall dass das Bildformat quadratisch ist wird in dem Code auch nicht behandelt.
Hier der nachgebesserte Code.
Java:
import javax.swing.JFrame;

public class Start {
    public static void main(String[] args) {
        JFrame frame = new JFrame("Image Load");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add(new ImageSetPanel(600, 600));
        frame.pack();
        frame.setResizable(false);
        frame.setVisible(true);
    }
}
Java:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import javax.swing.BorderFactory;
import javax.swing.JPanel;

@SuppressWarnings("serial")
public class ImagePanel extends JPanel {
    private Image img;
    private Point pos = new Point();

    public ImagePanel(int width, int height) {
        setPreferredSize(new Dimension(width, height));
        setBackground(Color.DARK_GRAY);
        setBorder(BorderFactory.createLineBorder(Color.WHITE, 2));
    }

    private void centerImage(int width, int height) {
        pos.x = 0;
        pos.y = 0;
        if (width < getWidth())
            pos.x = (getWidth() - width) / 2;
        if (height < getHeight())
            pos.y = (getHeight() - height) / 2;
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (img != null)
            g.drawImage(img, pos.x, pos.y, this);
    }

    /**
     * Sets an image to be displayed. The image automatically adjusts to the
     * available size. Important: Call this method only after the layout manager has
     * calculated the available space. Otherwise, the specific available size isn't
     * yet available, and the image won't be set.
     *
     * @param img Image to be set.
     */
    public void setImage(Image img) {
        int width = getWidth();
        int height = getHeight();
        if (img == null || width == 0 || height == 0)
            return;
        int imgW = img.getWidth(this);
        int imgH = img.getHeight(this);
        float rel = imgW / (float) imgH;
        if (imgH != imgW) {
            if (imgW < imgH)
                width = (int) (height * rel);
            else
                height = (int) (width / rel);
        } else {
            if (width < height)
                height = width;
            else
                width = height;
        }
        this.img = img.getScaledInstance(width, height, Image.SCALE_SMOOTH);
        centerImage(width, height);
    }

}
Java:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Image;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.filechooser.FileNameExtensionFilter;

@SuppressWarnings("serial")
public class ImageSetPanel extends JPanel {
    private JFileChooser chooser = new JFileChooser();
    private JButton open = new JButton("load Image");
    private ImagePanel imgPanel;

    public ImageSetPanel(int width, int height) {
        setPreferredSize(new Dimension(width, height));
        setLayout(new BorderLayout(5, 5));
        imgPanel = new ImagePanel(height, (int) (height * .9));
        add(open, BorderLayout.NORTH);
        add(imgPanel, BorderLayout.CENTER);
        chooser.setFileFilter(new FileNameExtensionFilter("JPG & GIF Images", "jpg", "gif"));
        open.addActionListener(e -> {
            int ok = chooser.showOpenDialog(this);
            if (ok == JFileChooser.APPROVE_OPTION) {
                Image img;
                try {
                    img = ImageIO.read(chooser.getSelectedFile());
                } catch (IOException e1) {
                    JOptionPane.showMessageDialog(this, "Can't load " + chooser.getSelectedFile().getName());
                    return;
                }
                imgPanel.setImage(img);
                repaint();
            }
        });
    }
}
 
Vielen Dank @Blender3D
Habe den Chooser auskommentiert und dafür dem img einen fixen Pfad gesetzt!
Später werde ich das mit getter und setter lösen
Ich denke nicht, dass das zu meinem Problem führt.


Ebenso habe ich:
Java:
JOptionPane.showMessageDialog(this, "Can't load " + chooser.getSelectedFile().getName());
ersetzt durch:
Code:
e.printStackTrace();
und da wirft er mir den Fehler:
java.lang.IllegalArgumentException: Width (0) and height (0) must be non-zero
at java.desktop/java.awt.image.ReplicateScaleFilter.<init>(ReplicateScaleFilter.java:102)
at java.desktop/java.awt.image.AreaAveragingScaleFilter.<init>(AreaAveragingScaleFilter.java:77)
at java.desktop/java.awt.Image.getScaledInstance(Image.java:172)
at imageToPanel.ImagePanel.setImage(ImagePanel.java:75)
at imageToPanel.ImageSetPanel.<init>(ImageSetPanel.java:28)
at imageToPanel.Start.main(Start.java:10)

Wenn ich jetzt aber folgendes mache,
Darum muss ich in der Klasse: ImagePanel() alle
getHeight() durch > getPreferredSize().height und
getWidth() durch > getPreferredSize().width
ersetzen.
funktioniert es einwandfrei! 😁
Vielen Dank! Für mich passt es so! 👍

Zu erwähnen ist noch:
Mein System: Linux Mint 22.2, Mate
und ich kompiliere für "OpenJDK 13"

Das ganze muss nur auf Linux laufen.
 
Zuletzt bearbeitet:
Wenn ich jetzt aber folgendes mache,
funktioniert es einwandfrei! 😁
Vielen Dank! Für mich passt es so! 👍
Es wird auch in den meisten Fällen funktionieren. Falls du aber die Klasse ImagePanel mit einer zu Großen Dimension verwendest. z.B. Dein Bildschirm hat eine Auflösung von 800 x 600. Du erzeugst aber einen ImagePanel mit 1000 x 600 dann wird das Bild nicht mehr richtig an die vorhandene Größe angepasst. Oder du setzt neben dem ImagePanel andere Komponenten in dasselbe Layout die ebenfalls Platz benötigen und es bleibt für das ImagPanel zu wenig Platz übrig. --> der Fehler deiner Änderung macht sich bemerkbar.
Hier ist das Beispielprogramm das ebenfalls funktioniert ohne getPreferredSize().height da die Klasse, so wie gedacht, erst das Image setzt nachdem der LayoutManager die vorhandene Dimension ermittelt hat. Probiere das angehängte File aus.
PS:
Ich glaub ich hab wieder was gelernt:
Das ganze ist noch nicht visible!
Darum muss ich in der Klasse: ImagePanel() alle
getHeight() durch > getPreferredSize().height und
getWidth() durch > getPreferredSize().width
ersetzen.
Wenn du den obigen Einwand verstehst dann hast du wirklich etwas gelernt. 😉
 

Anhänge

Wenn du den obigen Einwand verstehst dann hast du wirklich etwas gelernt. 😉
Vielen Dank habs kapiert! 😇

Mein Fehler lag darin, dass ich das Bild mit einem statischen Pfad gleich beim Aufruf der Form schon anzeigen wollte...
Dein "centerImage.jar" läd das Bild aber erst nach aufruf des Chooser.

getHeight() resp. getWidth() liefert die aktuelle Grösse der angezeigten Komponent. (visible ist bereits true, ansonten immer 0)
getPreferredSize().height resp. getPreferredSize().width ist die einmal gesetzte Grösse (bis zu einem erneuten programmgesteuerten setzen)
diese bleibt auch gleich, im gegensatz zu getSize(), wenn ich z.B. das JFrame vergrössere oder verkleinere.

Nochmals vielen Dank für Deine Geduld...🤝
 
PS:
Da der Bild-Pfad schon in der aufrufenden Form bekannt ist, habe ich es so gelösst:
Bei mir hat die ImageSetPanel-Klasse jetzt folgenden Code:
Java:
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Image;
import java.io.File;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;

@SuppressWarnings("serial")
public class ImageSetPanel extends JPanel {
//    JFileChooser chooser = new JFileChooser();

//    private JButton open = new JButton("load Image");
    private ImagePanel imgPanel;

    public ImageSetPanel(int width, int height, String imagePfad) {
        setPreferredSize(new Dimension(width, height));
        setLayout(new BorderLayout(5, 5));
        imgPanel = new ImagePanel(width, height);
        
        add(imgPanel, BorderLayout.CENTER);
        Thread t = new Thread(new Runnable()
        {
            public void run()
            {
                SwingUtilities.invokeLater(new Runnable()
                {
                    public void run()
                    {
                        setIMG(imagePfad);
                    }
                });
            }
        });
        t.start();       
    }
    
    public void setIMG(String forFile) {
        //"/media/administrator/parts/DATEN/Bilder/sR0n_rWd_UOvQ2sFTdibzQ_1.jpg"
        File f = new File(forFile);
        Image img = null;
        try {
            img = ImageIO.read(f);
            imgPanel.setImage(img);
            repaint();
        } catch (Exception e) {
            e.printStackTrace();
//            JOptionPane.showMessageDialog(this, "Datei:\n" + f.getName()+"\nnicht gefunden!!!");
        }

    }
}
Ich hoffe das ist jetzt der richtige Ansatz...

Es wird alles korrekt dargestellt und ich verwende Deine ImagePanel-Klasse
ohne getPreferredSize() !!! (einfach nur getWidth() und getHeight())
 

Zurück
Oben