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

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

问题描述

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

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

我在网上找到了类似的主题,但我找不到完全相同的问题 解释 和尽可能简单的解决方案.我还查看了 EDTSwingWorker 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();
        }
    }
}

由这段代码调用:

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

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

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天全站免登陆