代码之家  ›  专栏  ›  技术社区  ›  1279343

为什么这个JLabel的ImageIcon没有更新?

  •  0
  • 1279343  · 技术社区  · 10 年前

    SSCCE,尽我所能的小,保持所有逻辑的相同顺序:

    import java.awt.Color;
    import java.awt.Graphics2D;
    import java.awt.event.MouseEvent;
    import java.awt.event.MouseListener;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import javax.imageio.ImageIO;
    import javax.swing.ImageIcon;
    import javax.swing.JFileChooser;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    
    public class Test {
    
        public static void main(String[] args) {
            new Test();
        }
    
        BufferedImage img = null; // <-- needs this scope
        JFrame mainWindow = new JFrame();
        JLabel mainImage = new JLabel();
    
        public Test() {
            mainWindow.add(mainImage);
            mainWindow.setVisible(true);
            mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    // step 5
            mainImage.addMouseListener(new MouseListener() {
                @Override public void mouseClicked(MouseEvent e) {
                    dostuff();
                }
                @Override public void mouseEntered(MouseEvent e) {}
                @Override public void mouseExited(MouseEvent e) {}
                @Override public void mousePressed(MouseEvent e) {}
                @Override public void mouseReleased(MouseEvent e) {}
            });
            dostuff();
        }
    
        private void dostuff() {
    // step 1
            try {
                JFileChooser fc = new JFileChooser();
                fc.showOpenDialog(null);
                File file = fc.getSelectedFile();
                img = ImageIO.read(file);
            } catch (Exception e) {
                e.printStackTrace(); 
                System.exit(1);
            }
    //step 2
            mainImage.setIcon(new ImageIcon(img));
            mainWindow.pack();
            mainWindow.setLocationRelativeTo(null);
            Graphics2D g = img.createGraphics();
            g.setColor(new Color(0xFFFF0000));
            g.drawOval(10, 10, 10, 10);
            try{Thread.sleep(2000);}catch(Exception e){}
    // step 3
            BufferedImage img2 = new BufferedImage(400,300,BufferedImage.TYPE_INT_RGB);
            for (int i = 10 ; i < 20 ; i++) {
                for (int j = 10 ; j < 20 ; j++) {
                    img2.setRGB(i,j,0xFF0000FF);
                }
            }
    // step 4
            mainImage.setIcon(new ImageIcon(img2));
            mainWindow.pack();
            mainWindow.setLocationRelativeTo(null);
        }
    
    }
    

    我想做的事情应该是显而易见的,而编译将表明它并没有做到这一点。但我希望这篇文章有一个问号,所以这里是问题描述和问题:

    我希望发生的事情:

    1. 程序加载,并提示用户选择文件(图像)。

    2. 选择一个图像后,该图像将显示在JFrame中,并在其上绘制一些Graphics2D图形。(我包括sleep(),因为这些绘图在实际程序中需要一段时间)

    3. 完成绘图后,将创建一个新图像并在其上绘制。

    4. 新图像替换JFrame中的旧图像

    5. 当用户单击图像时,系统会提示他们选择一个新图像,我们从步骤1开始重复。

    正在发生的情况:

    步骤1到步骤4工作正常。在第五步,选择图像后,JFrame由用户选择的图像大小的黑色矩形填充,第三步的图像重叠在左上角,第二步没有发生,第三到第五步看起来工作正常。

    此外,在我的实际程序中,我可以通过第三步产生的输出来判断新用户选择的图像是否正常工作。

    所以问题是,我如何修复稍后重复的第二步,以便在JFrame中显示适当的图像?

    编辑:这张imgur专辑一步步展示了我正在取得的成果。 http://imgur.com/a/xW051

    EDIT2:在没有Thread.sleep()的情况下更新

    import java.awt.Color;
    import java.awt.Graphics2D;
    import java.awt.event.MouseEvent;
    import java.awt.event.MouseListener;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import javax.imageio.ImageIO;
    import javax.swing.ImageIcon;
    import javax.swing.JFileChooser;
    import javax.swing.JFrame;
    import javax.swing.JLabel;
    
    public class Test {
    
        public static void main(String[] args) {
            new Test();
        }
    
        BufferedImage img = null; // <-- needs this scope
        JFrame mainWindow = new JFrame();
        JLabel mainImage = new JLabel();
    
        public Test() {
            mainWindow.add(mainImage);
            mainWindow.setVisible(true);
            mainWindow.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    // step 5
            mainImage.addMouseListener(new MouseListener() {
                @Override public void mouseClicked(MouseEvent e) {
                    dostuff();
                }
                @Override public void mouseEntered(MouseEvent e) {}
                @Override public void mouseExited(MouseEvent e) {}
                @Override public void mousePressed(MouseEvent e) {}
                @Override public void mouseReleased(MouseEvent e) {}
            });
            dostuff();
        }
    
        private void dostuff() {
    // step 1
            try {
                JFileChooser fc = new JFileChooser();
                fc.showOpenDialog(null);
                File file = fc.getSelectedFile();
                img = ImageIO.read(file);
            } catch (Exception e) {
                e.printStackTrace(); 
                System.exit(1);
            }
    //step 2
            mainImage.setIcon(new ImageIcon(img));
            mainWindow.pack();
            mainWindow.setLocationRelativeTo(null);
            Graphics2D g = img.createGraphics();
            g.setColor(new Color(0xFFFF0000));
            for (int h = 0 ; h < 0xFF ; h++) {
                for (int i = 0 ; i < img.getWidth() ; i++) {
                    for (int j = 0 ; j < img.getHeight()/2 ; j++) {
                        img.setRGB(i,j,0x88FF0000 + h);
                    }
                }
                mainImage.repaint();
            }
    // step 3
            BufferedImage img2 = new BufferedImage(400,300,BufferedImage.TYPE_INT_RGB);
            for (int i = 10 ; i < 20 ; i++) {
                for (int j = 10 ; j < 20 ; j++) {
                    img2.setRGB(i,j,0xFF0000FF);
                }
            }
    // step 4
            mainImage.setIcon(new ImageIcon(img2));
            mainWindow.pack();
            mainWindow.setLocationRelativeTo(null);
        }
    
    }
    
    1 回复  |  直到 10 年前
        1
  •  2
  •   camickr    10 年前

    编辑:

    如果我加载一张1000x1000的图像,我会看到一个1000x1000黑色正方形。。。

    我猜黑色图像是因为您导致事件调度线程(EDT)休眠。因此,尽管框架正在使用pack()语句调整大小,但新加载的图像没有被绘制。

    不要使用线程。sleep()在EDT上执行的代码中。从侦听器执行的所有代码都在EDT上执行。相反,您应该使用 Swing Timer 以安排事件以更新标签的图像。

    编辑2:

    阅读Swing教程中的部分 Concurrency 。通常,所有事件代码都在EDT上执行。当EDT被阻挡时,Swing组件不能重新喷漆。

      mainImage.setIcon(new ImageIcon(img));
    

    上述语句将导致在“mainImage”标签上调用重新绘制()。重新绘制()请求被传递给RepaintManager,并将绘制请求添加到EDT的末尾。

    因此,标签将在“doStuff()”方法中的所有代码执行完毕后绘制。

    但是,您也可以调用

     mainImage.setIcon(new ImageIcon(img2));
    

    在方法的末尾。因此,当图像实际被绘制时,其图标已被第二次更改,因此仅绘制第二个图标。

    在这两个语句之间,读取图像后,调用pack()方法时调整帧的大小。我相信这是因为框架的打包导致了OS级的绘制,因为框架是一个OS小部件,而不是Swing组件。换句话说,即使框架上的组件没有重新绘制,也可以调整框架的大小。

    如果你想要一个快速响应的GUI,那么你就不能在EDT上执行长时间运行的代码。在第二个SSCCE的情况下,您添加的“for循环”需要很长时间 这将有效阻止EDT并防止新读取的图标被绘制。

    因此,长时间运行的任务应在单独的 Thread 。并发教程将解释如何使用 SwingWorker 对于长时间运行的任务。