Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/376.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何在JFrame中正确刷新图像?_Java_Multithreading_Swing_Jframe_Jlabel - Fatal编程技术网

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

Java 如何在JFrame中正确刷新图像?,java,multithreading,swing,jframe,jlabel,Java,Multithreading,Swing,Jframe,Jlabel,这是一个困扰我几个小时的问题,我自己也找不到解决办法 我在网上找到了类似的话题,但我找不到解释得很好、尽可能简单的解决方案的完全相同的问题。我也看过EDT和SwingWorker API文档,但对我来说太复杂了:( 那么,让我们开门见山吧。我有一个简单的JFrame,里面有JLabel,它由我的图像组成: private static class MyJLabel extends JLabel { private ImageIcon img = null; public MyJ

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

我在网上找到了类似的话题,但我找不到解释得很好、尽可能简单的解决方案的完全相同的问题。我也看过EDT和SwingWorker 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++;
}
buffer=receiveImage(in);//下载图像
MyJFrame f=null;
invokeLater(f=newmyjframe(buffer,“RDP”);
int x=0;

虽然(x个人而言,我会在将其应用于标签之前调整其大小,或者使用
JPanel
来执行绘制。
JLabel
具有很多功能

举个例子,您遇到的问题是,实际上您正在使用
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));
您将其初始化为

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));
需要注意的是,如果您使用了
JLabel
图标
支持,则此

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
的东西,并根据面板的当前大小缩放原始图像,例如

现在,通常情况下,在执行图像缩放时,我更喜欢使用中演示的分而治之方法,但为了简单起见,在本例中,我只使用了and
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
用法,这对我来说相当神奇。 我仍在使用脏方法调用此代码:


我的问题是:我如何将changeImage方法与
EDT
一起使用?

为什么要在标签上提供图标并让它自己绘制的情况下覆盖JLabel来绘制图像。只需将图像包装在图标图像中并使用setIcon来更改图像?@mad程序员:我需要覆盖paintComponent方法来调整大小能力。我相信您的changeImage应该在
EDT
中运行,而且您可以使用
SwingWorker
,而且非常容易。个人而言,我会调整它的大小并将其应用于您,或者更快地获得更好的帮助,发布一个。使用SwingUtilities#invokeLater或SwingWorker