[英]How to properly refresh image in JFrame?
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 find exact same problem with well explained and as simple as possible solution. 我在网上找到了类似的主题,但我找不到完全相同的问题,并且解释得很好,解决方案尽可能简单。 I've also looked at EDT and SwingWorker API docs, but it was far too complicated for me :( 我也看过EDT和SwingWorker API文档,但对我来说太复杂了:(
So, let's get to the point. 所以,让我们谈谈这一点。 I have a simple JFrame with JLabel inside, that consist of my image: 我有一个简单的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();
}
}
}
It's invoked by this piece of code: 它由这段代码调用:
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++;
}
Unfortunately, my approach with changeImage method does not work - nothing happens (GUI starts but never gets updated). 不幸的是,我使用changeImage方法的方法不起作用 - 没有任何反应(GUI启动但永远不会更新)。
I'd appreciate little help with this. 我对此感激不尽。 Simple , working example with proper explanation would be appreciated the most ;) 简单 ,有正确解释的工作示例将受到最多的赞赏;)
Greetings! 问候!
Personally, I would either resize it before applying it to the label or use a JPanel
to perform the painting. 就个人而言,我会在将其应用到标签之前调整大小,或者使用JPanel
来执行绘画。 JLabel
has to much functionality dragging around with it. JLabel
有很多功能拖延它。
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 例如,您遇到的问题是您实际上是在使用setIcon
来设置图像,而是使用paintComponent
在其顶部绘制另一个(初始)图像
Your custom label takes a ImageIcon
as a inital parameter and paints it as such... 您的自定义标签将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);
}
}
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... 需要注意的是,如果你使用了JLabel
的Icon
支持,那......
label.setPreferredSize(dims);
Would be irrelevant as the JLabel
would use the icon size to determine it's preferred size...but any way... 将无关紧要,因为JLabel
会使用图标大小来确定它的首选尺寸......但无论如何......
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 应该指出的是,基于你的例子,这实际上是在EDT之外调用的,这很危险并且可能导致油漆肮脏
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... 但是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);
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... 就个人而言,我要做的是创建一个自定义组件,使用类似JPanel
东西,并根据面板的当前大小缩放原始图像,例如......
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 现在,通常,在执行图像缩放时,我更喜欢使用Java中演示的分割和确认方法:保持JPanel背景图像的纵横比 ,但是对于这个例子,我简单地使用了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;
}
}
}
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. 这是@MadProgrammer提供的示例中非常直接的复制粘贴。
The only thing left is EDT
usage, which is rather magic for me. 唯一剩下的是EDT
使用,这对我来说相当神奇 。 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
? 我的问题是:如何在EDT
使用changeImage方法?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.