Java 如何在JFrame中正确刷新图像?
这是一个困扰我几个小时的问题,我自己也找不到解决办法 我在网上找到了类似的话题,但我找不到解释得很好、尽可能简单的解决方案的完全相同的问题。我也看过EDT和SwingWorker API文档,但对我来说太复杂了:( 那么,让我们开门见山吧。我有一个简单的JFrame,里面有JLabel,它由我的图像组成:Java 如何在JFrame中正确刷新图像?,java,multithreading,swing,jframe,jlabel,Java,Multithreading,Swing,Jframe,Jlabel,这是一个困扰我几个小时的问题,我自己也找不到解决办法 我在网上找到了类似的话题,但我找不到解释得很好、尽可能简单的解决方案的完全相同的问题。我也看过EDT和SwingWorker API文档,但对我来说太复杂了:( 那么,让我们开门见山吧。我有一个简单的JFrame,里面有JLabel,它由我的图像组成: private static class MyJLabel extends JLabel { private ImageIcon img = null; public MyJ
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();
}
}
}
它由以下代码调用:
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++;
}
buffer=receiveImage(in);//下载图像
MyJFrame f=null;
invokeLater(f=newmyjframe(buffer,“RDP”);
int x=0;
虽然(x个人而言,我会在将其应用于标签之前调整其大小,或者使用JPanel
来执行绘制。JLabel
具有很多功能
举个例子,您遇到的问题是,实际上您正在使用setIcon
设置图像,但使用paintComponent
在图像顶部绘制另一个(初始)图像
您的自定义标签将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);
}
}
label = new MyJLabel(new ImageIcon(img));
您将其初始化为
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);
}
}
label = new MyJLabel(new ImageIcon(img));
需要注意的是,如果您使用了JLabel
的图标
支持,则此
label.setPreferredSize(dims);
与此无关,因为JLabel
将使用图标大小来确定其首选大小…但无论如何
然后使用以下命令更新图标
img = image;
if (label != null) {
label.setIcon(new ImageIcon(img));
label.repaint();
}
应该指出的是,根据您的示例,这实际上被称为EDT之外的,这是危险的,可能会导致油漆变脏
但是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);
已更新
就我个人而言,我要做的是创建一个自定义组件,使用类似于JPanel
的东西,并根据面板的当前大小缩放原始图像,例如
现在,通常情况下,在执行图像缩放时,我更喜欢使用中演示的分而治之方法,但为了简单起见,在本例中,我只使用了andAffineTransform
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;
}
}
}
这就是我想到的:
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);
}
}
从@MadProgrammer提供的示例中复制粘贴非常简单
剩下的就是EDT
用法,这对我来说相当神奇。
我仍在使用脏方法调用此代码:
我的问题是:我如何将changeImage方法与EDT
一起使用?为什么要在标签上提供图标并让它自己绘制的情况下覆盖JLabel来绘制图像。只需将图像包装在图标图像中并使用setIcon来更改图像?@mad程序员:我需要覆盖paintComponent方法来调整大小能力。我相信您的changeImage应该在EDT
中运行,而且您可以使用SwingWorker
,而且非常容易。个人而言,我会调整它的大小并将其应用于您,或者更快地获得更好的帮助,发布一个。使用SwingUtilities#invokeLater或SwingWorker