尝试从另一个应用程序获取图像,通过套接字发送字节数组,将其转换为BufferedImage
并在GUI中设置JLabel
,每3秒更新一次。
我尝试在论坛上查找,但有关图形更新的问题经常发生在我身上,而且我似乎从来没有把它弄好 - 在Java中至少有6种更新方法用于图形界面,每一个我试过赢得& #39;工作。
我知道问题不在于与客户端的连接,因为我可以使用ImageIO.write()
轻松保存我收到的图像,并且每3秒更新一次我希望收到的图像。我无法正确地更新JLabel
Java而无需去论坛并向人们询问。我猜这么复杂的任务。以下是代码:http://pastebin.com/95nMGLvZ。我在Netbeans做这个项目,所以那里有很多东西没有必要阅读,因为它与问题没有直接关系。
我认为答案在于创建一个单独的主题,以便从JLabel
收到的不断变化的BufferedImage
更新我的ObjectInputStream
。有人介意给我这个吗?对我的代码有什么好处? SwingWorker
,Threading
(wtf是setDaemon(flag)?),Runnable
,Timer
,invokeLater
?试过这一切。显然不正确。
EDIT1: 试了你的回答immibis:
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()返回,这意味着输入未正确初始化或连接未同步。我希望它是第一个选择,因为我不知道如何处理最后一个选项。
EDIT2:
尝试了你的回答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。我在这里没有选择吗?
EDIT3: 试过乔丹的代码:
@Override
public void paint(Graphics g) {
g.drawImage(biGlobal, 0, 0, null);
}
GUI类型崩溃了,并且正在绘制&#34;当我将鼠标光标悬停在它上面时的组件。当我启动代码时,它没有崩溃(+1)但它没有绘制任何东西,即使我试图将光标悬停在应该绘制BufferedImage的位置。也许我应该在revalidate()
方法中调用覆盖repaint
后添加paint(getGraphics())
或startRunning()
?
EDIT4:代码实际所在的while(true)
可能是问题但是当我使用SwingTimer时它与客户端不同步并在第一个周期后崩溃。有什么替代方案吗?
答案 0 :(得分:1)
一般来说,您有生产者/消费者模式。有些东西正在产生图像,有些东西想要消费图像。通常情况下,消费者会等待生产者告诉它已生成的东西,但在这种情况下,我们可以使用观察者模式,让生产者通知消费者已经生成了某些东西(而不是等待它)
我们需要一些生产者与消费者沟通......
public interface PictureConsumer {
public void newPicture(BufferedImage img);
}
您可以在UI代码中创建此实现,然后设置icon
JLabel
属性
现在,我们需要一些东西来制作图像...
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
答案 1 :(得分:0)
要更新标签,您需要确保使用EDT线程,因此请使用您收到BufferedImage的代码中的SwingUtilities.invokeLater
(理想情况下,它位于单独的“工作”线程中:
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
// Update your label here
}
});
答案 2 :(得分:0)
你想要完成的是什么。所有人可能会有更好的方式。
例如,将JLabel
替换为JPanel
,然后使用JPanel
绘制方法
@Override
public void paint(Graphics g) {
g.drawImage(Img, xcord, ycord, null);
}
然后让JPanel
实现runnable并在该run方法中进行更新。
JPanel类
public class GraphicsPanel extends JPanel{
private BufferedImage img;
@Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D G2D = (Graphics2D) g;
G2D.drawImage(img, 0, 0, null);
}
public void setImg(BufferedImage img) {
this.img = img;
}
}
然后确保从您希望调用其方法的位置可以看到此面板。 这看起来像这样
GraphicsPanel graphicsPanel = new GraphicsPanel();
boolean running;
BufferedImage srcImage;
public void run(){
while(running){
graphicsPanel.setImg(srcImage);
graphicsPanel.repaint();
try {
Thread.sleep(300);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}