为什么这个 JLabel 的 ImageIcon 没有更新?
Why is this JLabel's ImageIcon not updating?
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);
}
}
我想做什么应该很明显,编译会显示它没有这样做。但是我希望这个 post 有一个问号所以这里是问题描述和问题:
我想要发生的事情:
程序加载,并提示用户select一个文件(一张图片)。
在 selecting 图像时,该图像显示在 JFrame 中,并且在其上绘制一些 Graphics2D 绘图。 (我包含了 sleep() 因为这些绘图在实际程序中需要一段时间)
绘图完成后,将创建一个新图像并在其上绘制。
新图片替换JFrame中的旧图片
当用户点击图片时,会提示他们select一张新图片,我们从第一步开始重复。
发生了什么:
第一步到第四步工作正常。在第五步,在 select 图像之后,JFrame 由一个黑色矩形填充,该矩形大小与用户 select 编辑的图像相同,左上角覆盖了第三步中的图像,步骤第二步没有发生,第三步到第五步似乎工作正常。
更重要的是,在我的实际程序中,我可以通过第三步产生的输出告诉新用户-selected-image 正在很好地完成它的工作。
所以问题是,我如何才能修复第二步的后续重复,以便在 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);
}
}
编辑:
If I then load a 1000x1000 image, I see a 1000x1000 black square...
我猜黑色图像是因为您导致事件调度线程 (EDT) 休眠。因此,尽管框架正在使用 pack() 语句调整大小,但新加载的图像并未被绘制。
不要在 EDT 上执行的代码中使用 Thread.sleep()。从侦听器执行的所有代码都在 EDT 上执行。相反,您应该使用 Swing Timer 来安排更新标签图像的事件。
编辑 2:
阅读 Concurrency 上的 Swing 教程部分。通常,所有事件代码都在 EDT 上执行。 EDT被阻塞时,Swing组件无法重绘
mainImage.setIcon(new ImageIcon(img));
上述语句将导致在 "mainImage" 标签上调用 repaint()。 repaint() 请求被传递给 RepaintManager 并在 EDT 的末尾添加一个绘画请求。
因此标签将在 "doStuff()" 方法中的所有代码执行完毕后绘制。
然而你也调用
mainImage.setIcon(new ImageIcon(img2));
在方法的最后。所以当图像实际绘制时,它的图标已经第二次更改,所以只绘制了第二个图标。
在这两个语句之间,在读取图像后调用 pack() 方法时调整框架的大小。我相信这是因为框架的打包导致 OS 级别绘制,因为框架是 OS 小部件而不是 Swing 组件。换句话说,即使框架上的组件没有重新绘制,框架也可以调整大小。
如果您想要响应式 GUI,则不能在 EDT 上执行长 运行ning 代码。在第二个 SSCCE 的情况下,您添加的 "for loop" 需要很长时间
到 运行 这有效地阻止了 EDT 并防止绘制新读取的图标。
因此长 运行ning 任务应该在单独的 Thread
上执行。并发教程将解释如何使用 SwingWorker
执行长 运行ning 任务。
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);
}
}
我想做什么应该很明显,编译会显示它没有这样做。但是我希望这个 post 有一个问号所以这里是问题描述和问题:
我想要发生的事情:
程序加载,并提示用户select一个文件(一张图片)。
在 selecting 图像时,该图像显示在 JFrame 中,并且在其上绘制一些 Graphics2D 绘图。 (我包含了 sleep() 因为这些绘图在实际程序中需要一段时间)
绘图完成后,将创建一个新图像并在其上绘制。
新图片替换JFrame中的旧图片
当用户点击图片时,会提示他们select一张新图片,我们从第一步开始重复。
发生了什么:
第一步到第四步工作正常。在第五步,在 select 图像之后,JFrame 由一个黑色矩形填充,该矩形大小与用户 select 编辑的图像相同,左上角覆盖了第三步中的图像,步骤第二步没有发生,第三步到第五步似乎工作正常。
更重要的是,在我的实际程序中,我可以通过第三步产生的输出告诉新用户-selected-image 正在很好地完成它的工作。
所以问题是,我如何才能修复第二步的后续重复,以便在 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);
}
}
编辑:
If I then load a 1000x1000 image, I see a 1000x1000 black square...
我猜黑色图像是因为您导致事件调度线程 (EDT) 休眠。因此,尽管框架正在使用 pack() 语句调整大小,但新加载的图像并未被绘制。
不要在 EDT 上执行的代码中使用 Thread.sleep()。从侦听器执行的所有代码都在 EDT 上执行。相反,您应该使用 Swing Timer 来安排更新标签图像的事件。
编辑 2:
阅读 Concurrency 上的 Swing 教程部分。通常,所有事件代码都在 EDT 上执行。 EDT被阻塞时,Swing组件无法重绘
mainImage.setIcon(new ImageIcon(img));
上述语句将导致在 "mainImage" 标签上调用 repaint()。 repaint() 请求被传递给 RepaintManager 并在 EDT 的末尾添加一个绘画请求。
因此标签将在 "doStuff()" 方法中的所有代码执行完毕后绘制。
然而你也调用
mainImage.setIcon(new ImageIcon(img2));
在方法的最后。所以当图像实际绘制时,它的图标已经第二次更改,所以只绘制了第二个图标。
在这两个语句之间,在读取图像后调用 pack() 方法时调整框架的大小。我相信这是因为框架的打包导致 OS 级别绘制,因为框架是 OS 小部件而不是 Swing 组件。换句话说,即使框架上的组件没有重新绘制,框架也可以调整大小。
如果您想要响应式 GUI,则不能在 EDT 上执行长 运行ning 代码。在第二个 SSCCE 的情况下,您添加的 "for loop" 需要很长时间 到 运行 这有效地阻止了 EDT 并防止绘制新读取的图标。
因此长 运行ning 任务应该在单独的 Thread
上执行。并发教程将解释如何使用 SwingWorker
执行长 运行ning 任务。