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

时间:2015-08-01 16:41:14

标签: java image swing bufferedimage javax.imageio

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步开始重复。

  6. 发生了什么:

    第一步到第四步工作正常。在第五步,选择图像后,JFrame将填入一个用户选择图像大小的黑色矩形,第三步的图像覆盖在左上角,第二步不会发生,第三步到第五步似乎工作得很好。

    更重要的是,在我的实际程序中,我可以看出新的用户选择图像通过第三步产生的输出正常工作。

    所以问题是,如何修复第2步的后续重复,以便在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 个答案:

答案 0 :(得分:2)

编辑:

  

如果我然后加载1000x1000图像,我会看到一个1000x1000黑色方块......

我猜黑色图像是因为你导致事件调度线程(EDT)进入睡眠状态。因此,虽然框架使用pack()语句调整大小,但新加载的图像未被绘制。

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

编辑2:

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

  mainImage.setIcon(new ImageIcon(img));

上述语句将导致在&#34; mainImage&#34;上调用repaint()。标签。 repaint()请求被传递给RepaintManager,并且绘画请求被添加到EDT的末尾。

因此标签将在&#34; doStuff()&#34;中的所有代码之后绘制。方法已经完成执行。

但是你也可以调用

 mainImage.setIcon(new ImageIcon(img2));

在方法结束时。因此,当图像实际被绘制时,它的Icon已经被第二次更改,因此只绘制了第二个Icon。

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

如果您想要一个响应式GUI,那么您就无法在EDT上执行长时间运行的代码。在第二个SSCCE的情况下,&#34; for循环&#34;你补充说需要很长时间 运行,这有效地阻止了EDT并阻止了新读取的图标。

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