How to properly refresh image in JFrame?

前端 未结 2 834
无人及你
无人及你 2021-01-26 03:17

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 fin

相关标签:
2条回答
  • 2021-01-26 03:33

    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.

    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

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

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

    label.setPreferredSize(dims);
    

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

    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

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

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

    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

    SmallLarge

    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;
    
            }
    
        }
    
    }
    
    0 讨论(0)
  • 2021-01-26 03:38

    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.

    The only thing left is EDT usage, which is rather magic for me. 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?

    0 讨论(0)
提交回复
热议问题