如何在JFrame中正确刷新图像?

这是一个让我困扰几个小时的问题,我自己找不到解决办法……

我在网上找到了类似的主题,但我找不到完全相同的问题,并且解释得很好,解决方案尽可能简单。 我也看过EDTSwingWorker API文档,但对我来说这太复杂了:(

所以,让我们谈谈这一点。 我有一个简单的JFrame里面有JLabel,它由我的图像组成:

private static class MyJLabel extends JLabel { private ImageIcon img = null; public MyJLabel(ImageIcon img) { super(); this.img = img; } @Override public void paintComponent(Graphics g) { super.paintComponent(g); g.drawImage(img.getImage(), 0, 0, getWidth(), getHeight(), this); } } private static class MyJFrame extends JFrame implements Runnable { private BufferedImage img = null; private MyJLabel label = null; public MyJFrame(BufferedImage image, String title) { super(title); img = image; } @Override public void run() { Dimension dims = new Dimension(img.getWidth(), img.getHeight()); dims = new Dimension(dims.width / 2, dims.height / 2); label = new MyJLabel(new ImageIcon(img)); label.setPreferredSize(dims); addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { label.repaint(); } }); setLayout(new BorderLayout()); getContentPane().add(BorderLayout.CENTER, label); setLocation(200, 200); setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); pack(); setVisible(true); } public void changeImage(BufferedImage image) { img = image; if (label != null) { label.setIcon(new ImageIcon(img)); label.repaint(); } } } 

它由这段代码调用:

 buffer = receiveImage(in); // download image MyJFrame f = null; javax.swing.SwingUtilities.invokeLater(f = new MyJFrame(buffer, "RDP")); int x = 0; while (x <= 15) { txt.println("next"); // notify server that we are ready while (true) { // wait for server if (reader.readLine().equals("ready")) break; } buffer = receiveImage(in); // download image // do some magic here and refresh image somehow :( f.changeImage(buffer); // does not work! x++; } 

不幸的是,我使用changeImage方法的方法不起作用 – 没有任何反应(GUI启动但永远不会更新)。

我对此感激不尽。 简单 ,有正确解释的工作示例将受到最多的赞赏;)

问候!

就个人而言,我会在将其应用于标签之前resize,或者使用JPanel来执行绘画。 JLabel必须使用它来实现许多function。

例如,您遇到的问题是您实际上是使用setIcon来设置图像,而是使用paintComponent在其顶部绘制另一个(初始)图像

您的自定义标签将ImageIcon作为初始参数并将其绘制为…

 private static class MyJLabel extends JLabel { private ImageIcon img = null; public MyJLabel(ImageIcon img) { super(); this.img = img; } @Override public void paintComponent(Graphics g) { super.paintComponent(g); g.drawImage(img.getImage(), 0, 0, getWidth(), getHeight(), this); } } 

你初始化它……

 label = new MyJLabel(new ImageIcon(img)); 

需要注意的是,如果你使用了JLabelIcon支持,这……

 label.setPreferredSize(dims); 

因为JLabel会使用图标大小来确定它的首选尺寸……但无论如何……都无关紧要……

然后你用这个更新图标..

 img = image; if (label != null) { label.setIcon(new ImageIcon(img)); label.repaint(); } 

应该指出的是,基于你的例子,这实际上是在EDT之外调用的,这很危险并且可能导致脏漆

但是setIcon永远不会在MyLabel更改img的值,因此当调用paintComponent方法时,实际上是在绘制您在更新中提供的图标…

 // Paint the new Icon super.paintComponent(g); // Paint the old/initial image... g.drawImage(img.getImage(), 0, 0, getWidth(), getHeight(), this); 

更新

就个人而言,我要做的是创建一个自定义组件,使用类似JPanel东西,并根据面板的当前大小缩放原始图像,例如……

现在,通常,在执行图像缩放时,我更喜欢使用Java中演示的分割和配置方法:保持JPanel背景图像的纵横比 ,但是对于这个例子,我简单地使用和AffineTransform为了简单起见

小大

 import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.HeadlessException; import java.awt.Image; import java.awt.geom.AffineTransform; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class ScalableImageExample { public static void main(String[] args) { new ScalableImageExample(); } public ScalableImageExample() { EventQueue.invokeLater(new Runnable() { @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { ex.printStackTrace(); } try { ResizableImagePane pane = new ResizableImagePane(); pane.setImage(...); JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(pane); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } catch (IOException exp) { exp.printStackTrace(); } } }); } public class ResizableImagePane extends JPanel { private Image img; public ResizableImagePane() { } public void setImage(Image value) { if (img != value) { Image old = img; this.img = value; firePropertyChange("image", old, img); revalidate(); repaint(); } } public Image getImage() { return img; } @Override public Dimension getPreferredSize() { return img == null ? new Dimension(200, 200) : new Dimension(img.getWidth(this), img.getHeight(this)); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); if (img != null) { Graphics2D g2d = (Graphics2D) g.create(); int width = getWidth(); int height = getHeight(); double scaleFactor = getScaleFactorToFit(new Dimension(img.getWidth(this), img.getHeight(this)), getSize()); int x = (int)((width - (img.getWidth(this) * scaleFactor)) / 2); int y = (int)((height - (img.getHeight(this) * scaleFactor)) / 2); AffineTransform at = new AffineTransform(); at.translate(x, y); at.scale(scaleFactor, scaleFactor); g2d.setTransform(at); g2d.drawImage(img, 0, 0, this); g2d.dispose(); } } public double getScaleFactor(int iMasterSize, int iTargetSize) { return (double) iTargetSize / (double) iMasterSize; } public double getScaleFactorToFit(Dimension original, Dimension toFit) { double dScale = 1d; if (original != null && toFit != null) { double dScaleWidth = getScaleFactor(original.width, toFit.width); double dScaleHeight = getScaleFactor(original.height, toFit.height); dScale = Math.min(dScaleHeight, dScaleWidth); } return dScale; } } } 

这就是我提出的:

 private static class MyJPanel extends JPanel { private Image img = null; public MyJPanel() {} public void setImage(Image value) { if (img != value) { Image old = img; this.img = value; firePropertyChange("image", old, img); revalidate(); repaint(); } } public Image getImage() { return img; } @Override public Dimension getPreferredSize() { return img == null ? new Dimension(200, 200) : new Dimension(img.getWidth(this), img.getHeight(this)); } @Override protected void paintComponent(Graphics g) { super.paintComponent(g); if (img != null) { Graphics2D g2d = (Graphics2D) g.create(); int width = getWidth(); int height = getHeight(); double scaleFactor = getScaleFactorToFit(new Dimension(img.getWidth(this), img.getHeight(this)), getSize()); int x = (int) ((width - (img.getWidth(this) * scaleFactor)) / 2); int y = (int) ((height - (img.getHeight(this) * scaleFactor)) / 2); AffineTransform at = new AffineTransform(); at.translate(x, y); at.scale(scaleFactor, scaleFactor); g2d.setTransform(at); g2d.drawImage(img, 0, 0, this); g2d.dispose(); } } public double getScaleFactor(int iMasterSize, int iTargetSize) { return (double) iTargetSize / (double) iMasterSize; } public double getScaleFactorToFit(Dimension original, Dimension toFit) { double dScale = 1d; if (original != null && toFit != null) { double dScaleWidth = getScaleFactor(original.width, toFit.width); double dScaleHeight = getScaleFactor(original.height, toFit.height); dScale = Math.min(dScaleHeight, dScaleWidth); } return dScale; } } private static class MyJFrame extends JFrame implements Runnable { private BufferedImage img = null; private MyJPanel panel = null; public MyJFrame(BufferedImage image, String title) { super(title); img = image; } @Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {} panel = new MyJPanel(); panel.setImage(img); setLayout(new BorderLayout()); add(BorderLayout.CENTER, panel); setLocation(200, 200); setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE); pack(); setVisible(true); } public void changeImage(BufferedImage image) { if ((panel != null) && (panel.getImage() != image)) panel.setImage(image); } } 

这是@MadProgrammer提供的示例中非常直接的复制粘贴。

唯一剩下的是EDT使用,这对我来说相当神奇 。 我仍然使用脏方式调用此代码:

 MyJFrame mjf = null; javax.swing.SwingUtilities.invokeLater(mjf = new MyJFrame(buffer, "RDP")); ... mjf.changeImage(buffer); 

我的问题是:如何在EDT使用changeImage方法?