如何正确刷新JFrame中的图像? [英] How to properly refresh image in JFrame?

查看:105
本文介绍了如何正确刷新JFrame中的图像?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

这是一个困扰我几个小时的问题,我自己找不到解决方案...

This is a problem that disturbs me for few hours now and I'm not able to find a solution by myself...

我在网络上发现了类似的话题,但是我无法很好地解释解释并找到尽可能简单的解决方案来解决同样的问题.我也看过 EDT SwingWorker API文档,但是对我来说太复杂了:(

I've found similar topics all around the net, but I couldn't find exact same problem with well explained and as simple as possible solution. I've also looked at EDT and SwingWorker API docs, but it was far too complicated for me :(

所以,让我们开始讨论.我有一个带有JLabel的简单JFrame,其中包含我的图片:

So, let's get to the point. I have a simple JFrame with JLabel inside, that consist of my image:

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();
        }
    }
}

它是由这段代码调用的:

It's invoked by this piece of code:

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启动但从未更新).

Unfortunately, my approach with changeImage method does not work - nothing happens (GUI starts but never gets updated).

在此方面,我将不提供任何帮助. 简单,带有正确解释的工作示例,将是最受赞赏的;)

I'd appreciate little help with this. Simple, working example with proper explanation would be appreciated the most ;)

问候!

推荐答案

我个人而言,可以在将其应用于标签之前调整其大小,也可以使用JPanel进行绘画. JLabel具有很多功能.

Personally, I would either resize it before applying it to the label or use a JPanel to perform the painting. JLabel has to much functionality dragging around with it.

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

Case in point, the problem you're having is you're actually using setIcon to set the image, but using paintComponent to paint another (the initial) image over the top of it

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

Your custom label takes a ImageIcon as a inital parameter and paints it as such...

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

您可以这样初始化它...

You initialise it as such...

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

应注意,如果使用JLabelIcon支持,则此...

It should be noted that if you used the Icon support of JLabel, this...

label.setPreferredSize(dims);

无关紧要,因为JLabel会使用图标大小来确定其首选大小...但是无论如何...

Would be irrelevant as the JLabel would use the icon size to determine it's preferred size...but any way...

然后使用此图标更新图标.

Then you update the icon using this..

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

应该指出的是,根据您的示例,这实际上是在EDT之外被调用的,这很危险,并且可能导致油漆变脏

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

But setIcon never changes the value of img within MyLabel, so when your paintComponent method is called, you are actually painting over the icon you have supplied in the update...

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

已更新

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

Personally, what I would do is create a custom component, using something like a JPanel and scale the original image based on the current size of the panel, for example...

现在,通常在执行图像缩放时,我更喜欢使用分而治之的方法,如

Now, normally, when performing image scaling I prefer to use a divide and conqure approach as demonstrated in Java: maintaining aspect ratio of JPanel background image, but for this example, I've simply used and AffineTransform for simplicity sake

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;

        }

    }

}

这篇关于如何正确刷新JFrame中的图像?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆