尝试从另一个通过套接字发送字节数组的应用程序获取图像,并将其转换为 BufferedImage
并设置 JLabel
GUI 中每 3 秒更新一次。
我尝试在论坛上查找它,但是关于图形更新的问题对我来说经常出现,而且我似乎从来没有弄对过——java中至少有6种用于图形界面的更新方法,而我尝试过的每一种都不起作用。
我知道问题不在于与客户端的连接,因为我可以使用 ImageIO.write()
轻松保存收到的图像。它每 3 秒更新一次我期望收到的图像。我就是无法让 Java 更新 JLabel
正确地进行,而不必去论坛并询问人们。我想这是一项复杂的任务。这是代码:http://pastebin.com/95nMGLvZ 。我正在 Netbeans 中做这个项目,所以里面有很多东西没有必要阅读,因为它与问题没有直接关系。
我认为答案在于创建一个单独的线程来更新我的 JLabel
来自千变万化BufferedImage
这是从 ObjectInputStream
收到的。有人介意帮我做这件事吗?什么对我的代码更好? SwingWorker
, Threading
(wtf 是 setDaemon(flag) 吗?),Runnable
, Timer
, invokeLater
?尝试了这一切。显然不正确。
编辑1: 尝试了你的答案:
public void startRunning() {
try {
server = new ServerSocket(666, 10);
connection = server.accept();
networkStatus("Connected to " + connection.getInetAddress().getHostName());
Thread thr = new Thread(new Runnable() {
@Override
public void run() {
try {
input = new ObjectInputStream(connection.getInputStream());
} catch (IOException ex) {
JOptionPane.showMessageDialog(null, ex.toString());
}
}
});
thr.start();
System.out.println(!connection.isInputShutdown());
while (connection.isConnected()) {
try {
byte[] byteImage = (byte[]) input.readObject();
InputStream in = new ByteArrayInputStream(byteImage);
final BufferedImage bi = ImageIO.read(in);
jLabel_screen.setIcon(new ImageIcon(bi));
ImageIO.write(bi, "jpg", new File("C:\\Users\\User\\Desktop\\test.jpg"));
System.out.println("i'm working");
} catch (IOException ex) {
JOptionPane.showMessageDialog(null, ex.toString());
} catch (ClassNotFoundException ex) {
Logger.getLogger(SpyxServer.class.getName()).log(Level.SEVERE, null, ex);
}
}
} catch (IOException ex) {
JOptionPane.showMessageDialog(null, ex.toString());
}
}
它不起作用。它说byte[] byteImage = (byte[]) input.readObject();
线路有 NullPointerException
。唯一可以为 null 的值是 readObject() 的返回值,这意味着输入未正确初始化或连接未同步。我希望这是第一个选择,因为我不知道如何处理最后一个。
编辑2:
尝试了你的答案 blazetopher:
public void startRunning() throws IOException {
server = new ServerSocket(666, 10);
try {
connection = server.accept();
networkStatus("Connected to " + connection.getInetAddress().getHostName());
input = new ObjectInputStream(connection.getInputStream());
while (true) {
try {
byte[] byteImage = (byte[]) input.readObject();
InputStream in = new ByteArrayInputStream(byteImage);
final BufferedImage bi = ImageIO.read(in);
SwingUtilities.invokeLater(new Runnable() {//<-----------
@Override
public void run() {
jLabel_screen.setIcon(new ImageIcon(bi));
}
});
ImageIO.write(bi, "jpg", new File("C:\\Users\\User\\Desktop\\test.jpg"));
System.out.println("i'm working");
} catch (IOException ex) {
JOptionPane.showMessageDialog(null, ex.toString());
} catch (ClassNotFoundException ex) {
Logger.getLogger(SpyxServer.class.getName()).log(Level.SEVERE, null, ex);
}
}
} catch (EOFException eofException) {
networkStatus("Connection Closed. :(");
} finally {
input.close();
connection.close();
}
}
使用SwingUtilities.invokeLater
也没用。至少程序可以运行,甚至可以保存图像,但仍然无法更新 JLabel。我已经没有选择了吗?
编辑3: 尝试了乔丹的代码:
@Override
public void paint(Graphics g) {
g.drawImage(biGlobal, 0, 0, null);
}
当我将鼠标光标悬停在 GUI 上时,GUI 崩溃了并且正在“绘制”组件。当我启动代码时,它没有崩溃(+1),但它没有绘制任何内容,即使我尝试将光标悬停在应该绘制 BufferedImage 的位置也是如此。也许我应该添加 revalidate()
或repaint
调用覆盖后 paint(getGraphics())
里面startRunning()
方法?
编辑4:while(true)
代码实际上可能是问题所在,但是当我使用 SwingTimer 时,它与客户端不同步,并在第一个周期后崩溃。有什么替代方案吗?
最佳答案
一般来说,您有生产者/消费者模式。有些东西正在生成图像,有些东西想要消耗图像。通常,消费者会等待生产者告诉它已经生产了一些东西,但在这种情况下,我们可以使用观察者模式,让生产者通知消费者一些东西已经生产了(而不是等待它)
我们需要某种方式让生产者与消费者进行通信......
public interface PictureConsumer {
public void newPicture(BufferedImage img);
}
您将在 UI 代码中创建此实现,然后设置 JLabel
的 icon
属性
现在,我们需要一些东西来生成图像......
public class PictureProducer extends SwingWorker<Object, BufferedImage> {
private PictureConsumer consumer;
public PictureProducer(PictureConsumer consumer) {
this.consumer = consumer;
}
@Override
protected void process(List<BufferedImage> chunks) {
// Really only interested in the last image
BufferedImage img = chunks.get(chunks.size() - 1);
consumer.newPicture(img);
}
@Override
protected Object doInBackground() throws Exception {
/*
This whole setup worries me. Why is this program acting as the
server? Why aren't we pooling the image producer?
*/
try (ServerSocket server = ServerSocketFactory.getDefault().createServerSocket(666, 10)) {
try (Socket socket = server.accept()) {
try (ObjectInputStream ois = new ObjectInputStream(socket.getInputStream())) {
// Using `while (true)` is a bad idea, relying on the fact
// that an exception would be thrown when the connection is closed is
// a bad idea.
while (!socket.isClosed()) {
// Generally, I'd discourage using an ObjectInputStream, this
// is just me, but you could just as easily read directly from
// the ByteArrayInputStream...assuming the sender was sending
// the data as a byte stream ;)
byte[] bytes = (byte[]) ois.readObject();
try (ByteArrayInputStream bis = new ByteArrayInputStream(bytes)) {
BufferedImage img = ImageIO.read(bis);
publish(img);
}
}
}
}
}
return null;
}
@Override
protected void done() {
try {
get();
} catch (Exception e) {
e.printStackTrace();
JOptionPane.showMessageDialog(null, "Image Producer has failed: " + e.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
}
}
参见Worker Threads and SwingWorker了解更多详情
您可以举一个相反的例子(其中一些服务器正在生成图像,而客户端正在使用它们)here
关于java - 创建新线程以更新所述单独线程中的 JLabel 和 setIcon(),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30634011/