Is it possible to create a BufferedImage from a JPanel without first rendering it in a JFrame? I've searched everywhere I can think of and cannot find an answer. Can anyone help?
是否可以从JPanel创建BufferedImage而无需先在JFrame中呈现它?我搜遍了我能想到的任何地方,找不到答案。有人可以帮忙吗?
Here is some sample code. If I don't un-comment the JFrame code, my BufferedImage is blank.
这是一些示例代码。如果我不取消注释JFrame代码,我的BufferedImage是空白的。
test(){
// JFrame frame = new JFrame();
JPanel panel = new JPanel();
Dimension dim = new Dimension(50,50);
panel.setMinimumSize(dim);
panel.setMaximumSize(dim);
panel.setPreferredSize(dim);
JLabel label = new JLabel("hello");
panel.add(label);
// frame.add(panel);
// frame.pack();
BufferedImage bi = getScreenShot(panel);
//...code that saves bi to a jpg
}
private BufferedImage getScreenShot(JPanel panel){
BufferedImage bi = new BufferedImage(panel.getWidth(), panel.getHeight(), BufferedImage.TYPE_INT_ARGB);
panel.paint(bi.getGraphics());
return bi;
}
2 个解决方案
#1
8
See this answer to Swing: Obtain Image of JFrame as well as Why does the JTable header not appear in the image? for tips on painting components that have not yet been rendered. I expect the fix to your problem is shown in the label of LabelRenderTest.java
.
请参阅Swing的答案:获取JFrame的图像以及为什么JTable标题没有出现在图像中?有关绘制尚未渲染的组件的提示。我希望您的问题的修复程序显示在LabelRenderTest.java的标签中。
JLabel textLabel = new JLabel(title);
textLabel.setSize(textLabel.getPreferredSize());
Update
Dimension dim = new Dimension(50,50);
panel.setSize(dim); // very important!
panel.setMinimumSize(dim);
panel.setMaximumSize(dim);
panel.setPreferredSize(dim);
// ...
Or here is the complete source. The size of the label also needs to be set.
或者这里是完整的来源。还需要设置标签的大小。
import java.awt.Color;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class RenderTest {
RenderTest() {
JPanel panel = new JPanel();
panel.setBackground(Color.RED);
Dimension dim = new Dimension(50,50);
panel.setSize(dim);
panel.setMinimumSize(dim);
panel.setMaximumSize(dim);
panel.setPreferredSize(dim);
JLabel label = new JLabel("hello");
label.setSize(label.getPreferredSize());
panel.add(label);
BufferedImage bi = getScreenShot(panel);
JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(bi)));
}
private BufferedImage getScreenShot(JPanel panel){
BufferedImage bi = new BufferedImage(
panel.getWidth(), panel.getHeight(), BufferedImage.TYPE_INT_ARGB);
panel.paint(bi.getGraphics());
return bi;
}
public static void main(String[] args) {
new RenderTest();
}
}
#2
4
EDIT2: Basically Andrew Thompson is right in his answer, a frame is not necessary. At the same time it can be practical to have one, because a call to pack() will make the layout managers work. I deleted the first part of my original answer, left is the memory-related part. Note that calling dispose on the Graphics is still needed even without a frame.
编辑2:基本上安德鲁汤普森的答案是正确的,一个框架是没有必要的。同时拥有一个是实用的,因为对pack()的调用将使布局管理器工作。我删除了原始答案的第一部分,左边是与记忆相关的部分。请注意,即使没有帧,仍然需要在Graphics上调用dispose。
About your running out of heap space when using frames: this should not happen. Probably you only need to call dispose() on the frames when you are done with them. If that does not help, I would suggest asking it in a separate question. From the dispose docs:
关于使用框架时堆空间的用尽:这不应该发生。可能你完成后只需要在帧上调用dispose()。如果这没有帮助,我建议在一个单独的问题中提出这个问题。从dispose文档:
Releases all of the native screen resources used by this Window, its subcomponents, and all of its owned children. That is, the resources for these Components will be destroyed, any memory they consume will be returned to the OS, and they will be marked as undisplayable. (...) Note: When the last displayable window within the Java virtual machine (VM) is disposed of, the VM may terminate.
释放此Window,其子组件及其所有子组件使用的所有本机屏幕资源。也就是说,这些组件的资源将被销毁,它们使用的任何内存都将返回给操作系统,并且它们将被标记为不可显示。 (...)注意:当处理Java虚拟机(VM)中的最后一个可显示窗口时,VM可能会终止。
EDIT: some more thoughts:
编辑:更多的想法:
- reusing the same JFrame object should also work. Add panel, call pack, create image, remove panel, repeat
- 重用相同的JFrame对象也应该有效。添加面板,调用包,创建图像,删除面板,重复
- don't forget to call dispose() on the Graphics objects created by you as well
- 不要忘记在你创建的Graphics对象上调用dispose()
- In worst case you can restart the JVM from a script from time to time
- 在最坏的情况下,您可以不时地从脚本重新启动JVM
#1
8
See this answer to Swing: Obtain Image of JFrame as well as Why does the JTable header not appear in the image? for tips on painting components that have not yet been rendered. I expect the fix to your problem is shown in the label of LabelRenderTest.java
.
请参阅Swing的答案:获取JFrame的图像以及为什么JTable标题没有出现在图像中?有关绘制尚未渲染的组件的提示。我希望您的问题的修复程序显示在LabelRenderTest.java的标签中。
JLabel textLabel = new JLabel(title);
textLabel.setSize(textLabel.getPreferredSize());
Update
Dimension dim = new Dimension(50,50);
panel.setSize(dim); // very important!
panel.setMinimumSize(dim);
panel.setMaximumSize(dim);
panel.setPreferredSize(dim);
// ...
Or here is the complete source. The size of the label also needs to be set.
或者这里是完整的来源。还需要设置标签的大小。
import java.awt.Color;
import java.awt.Dimension;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class RenderTest {
RenderTest() {
JPanel panel = new JPanel();
panel.setBackground(Color.RED);
Dimension dim = new Dimension(50,50);
panel.setSize(dim);
panel.setMinimumSize(dim);
panel.setMaximumSize(dim);
panel.setPreferredSize(dim);
JLabel label = new JLabel("hello");
label.setSize(label.getPreferredSize());
panel.add(label);
BufferedImage bi = getScreenShot(panel);
JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(bi)));
}
private BufferedImage getScreenShot(JPanel panel){
BufferedImage bi = new BufferedImage(
panel.getWidth(), panel.getHeight(), BufferedImage.TYPE_INT_ARGB);
panel.paint(bi.getGraphics());
return bi;
}
public static void main(String[] args) {
new RenderTest();
}
}
#2
4
EDIT2: Basically Andrew Thompson is right in his answer, a frame is not necessary. At the same time it can be practical to have one, because a call to pack() will make the layout managers work. I deleted the first part of my original answer, left is the memory-related part. Note that calling dispose on the Graphics is still needed even without a frame.
编辑2:基本上安德鲁汤普森的答案是正确的,一个框架是没有必要的。同时拥有一个是实用的,因为对pack()的调用将使布局管理器工作。我删除了原始答案的第一部分,左边是与记忆相关的部分。请注意,即使没有帧,仍然需要在Graphics上调用dispose。
About your running out of heap space when using frames: this should not happen. Probably you only need to call dispose() on the frames when you are done with them. If that does not help, I would suggest asking it in a separate question. From the dispose docs:
关于使用框架时堆空间的用尽:这不应该发生。可能你完成后只需要在帧上调用dispose()。如果这没有帮助,我建议在一个单独的问题中提出这个问题。从dispose文档:
Releases all of the native screen resources used by this Window, its subcomponents, and all of its owned children. That is, the resources for these Components will be destroyed, any memory they consume will be returned to the OS, and they will be marked as undisplayable. (...) Note: When the last displayable window within the Java virtual machine (VM) is disposed of, the VM may terminate.
释放此Window,其子组件及其所有子组件使用的所有本机屏幕资源。也就是说,这些组件的资源将被销毁,它们使用的任何内存都将返回给操作系统,并且它们将被标记为不可显示。 (...)注意:当处理Java虚拟机(VM)中的最后一个可显示窗口时,VM可能会终止。
EDIT: some more thoughts:
编辑:更多的想法:
- reusing the same JFrame object should also work. Add panel, call pack, create image, remove panel, repeat
- 重用相同的JFrame对象也应该有效。添加面板,调用包,创建图像,删除面板,重复
- don't forget to call dispose() on the Graphics objects created by you as well
- 不要忘记在你创建的Graphics对象上调用dispose()
- In worst case you can restart the JVM from a script from time to time
- 在最坏的情况下,您可以不时地从脚本重新启动JVM