简体   繁体   English

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

[英]How to properly refresh image in JFrame?

This is a problem that disturbs me for few hours now and I'm not able to find a solution by myself... 这个问题让我困扰了几个小时,我自己找不到解决办法......

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 :( 我也看过EDTSwingWorker API文档,但对我来说太复杂了:(

So, let's get to the point. 所以,让我们谈谈这一点。 I have a simple JFrame with JLabel inside, that consist of my image: 我有一个简单的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();
        }
    }
}

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

Unfortunately, my approach with changeImage method does not work - nothing happens (GUI starts but never gets updated). 不幸的是,我使用changeImage方法的方法不起作用 - 没有任何反应(GUI启动但永远不会更新)。

I'd appreciate little help with this. 我对此感激不尽。 Simple , working example with proper explanation would be appreciated the most ;) 简单 ,有正确解释的工作示例将受到最多的赞赏;)

Greetings! 问候!

Personally, I would either resize it before applying it to the label or use a JPanel to perform the painting. 就个人而言,我会在将其应用到标签之前调整大小,或者使用JPanel来执行绘画。 JLabel has to much functionality dragging around with it. JLabel有很多功能拖延它。

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

Your custom label takes a ImageIcon as a inital parameter and paints it as such... 您的自定义标签将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);
    }
}

You initialise it as such... 你初始化它是......

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

It should be noted that if you used the Icon support of JLabel , this... 需要注意的是,如果你使用了JLabelIcon支持,那......

label.setPreferredSize(dims);

Would be irrelevant as the JLabel would use the icon size to determine it's preferred size...but any way... 将无关紧要,因为JLabel会使用图标大小来确定它的首选尺寸......但无论如何......

Then you update the icon using this.. 然后你用这个更新图标..

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

It should be pointed out, that based on your example, this is actually been called outside of the EDT, which is dangerous and could lead to a dirty paint 应该指出的是,基于你的例子,这实际上是在EDT之外调用的,这很危险并且可能导致油漆肮脏

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... 但是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);

Updated 更新

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

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 现在,通常,在执行图像缩放时,我更喜欢使用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;

        }

    }

}

This is what I came up with: 这就是我想出的:

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

It's pretty much straightforward copy-paste from example that @MadProgrammer provided. 这是@MadProgrammer提供的示例中非常直接的复制粘贴。

The only thing left is EDT usage, which is rather magic for me. 唯一剩下的是EDT使用,这对我来说相当神奇 I'm still invoking this code using dirty way : 我仍然使用脏方式调用此代码:

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

My question is: how do I use changeImage method with EDT ? 我的问题是:如何在EDT使用changeImage方法?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM