我想在无头模式下将JPanel绘制成BufferedImage(屏幕上根本没有GUI)。
final JPanel panel = createPanel();
panel.setSize(panel.getPreferredSize());
panel.validate();
// JFrame frame = new JFrame();
// frame.getContentPane().add(panel);
// frame.pack();
// frame.setVisible(true);
final BufferedImage image = new BufferedImage(
panel.getBounds().width,
panel.getBounds().height,
BufferedImage.TYPE_INT_ARGB
);
final Graphics2D gc = image.createGraphics();
gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
gc.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
try {
panel.paint(gc);
...save the image somewhere...
} finally {
gc.dispose();
}
但是我总是得到空图像,直到我将面板放入一个重量级的组件并在屏幕上显示(参见注释代码)。我不想展示它,这个应用程序在服务器上运行。
这是SSCCE:
public class Example {
private static JPanel createPanel() {
final JPanel panel = new JPanel(new GridBagLayout());
final JLabel label = new JLabel("Yeah, it's working!", SwingConstants.CENTER);
label.setFont(new Font("Arial", Font.PLAIN, 12));
final GridBagConstraints constraints = new GridBagConstraints();
constraints.fill = GridBagConstraints.BOTH;
constraints.weightx = 1;
constraints.weightx = 1;
panel.add(label, constraints);
return panel;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
final JPanel panel = createPanel();
panel.setSize(panel.getPreferredSize());
panel.validate();
// JFrame frame = new JFrame();
// frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// frame.getContentPane().add(panel);
// frame.pack();
// frame.setVisible(true);
final BufferedImage image = new BufferedImage(
panel.getBounds().width,
panel.getBounds().height,
BufferedImage.TYPE_INT_ARGB
);
final Graphics2D gc = image.createGraphics();
gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
gc.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
try {
panel.paint(gc);
ImageIO.write(image, "png", new File("image.png"));
} catch (IOException e) {
e.printStackTrace();
} finally {
gc.dispose();
}
}
});
}
}
答案 0 :(得分:3)
组件的大小为零,直到组件被实现,因此绘制方法不起作用。
结帐Screen Image。它将通过在面板上调用doLayout()来确保所有组件都具有有效大小,从而为您解决此问题。
答案 1 :(得分:3)
这是一个片段,它将简单标签绘制到图像文件,然后打开图像文件(如果在台式计算机上)。
import java.awt.Desktop;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JLabel;
public class Test {
public static void main(String[] args) throws IOException {
JLabel label = new JLabel("Hello world");
label.setSize(label.getPreferredSize());
BufferedImage image = new BufferedImage(label.getWidth(), label.getHeight(), BufferedImage.TYPE_INT_ARGB);
label.paint(image.getGraphics());
File output = new File("C:\\test\\hello world.png");
if (!output.getParentFile().exists()) {
output.getParentFile().mkdirs();
}
ImageIO.write(image, "png", output);
Desktop.getDesktop().open(output);
}
}
不要在面板上调用validate()
,而是调用doLayout()
(如果您有嵌套面板,请确保以递归方式调用它):
import java.awt.Desktop;
import java.awt.Font;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingConstants;
import javax.swing.SwingUtilities;
public class Example {
private static JLabel label;
private static JPanel createPanel() {
final JPanel panel = new JPanel(new GridBagLayout());
label = new JLabel("Yeah, it's working!", SwingConstants.CENTER);
label.setFont(new Font("Arial", Font.PLAIN, 12));
final GridBagConstraints constraints = new GridBagConstraints();
constraints.fill = GridBagConstraints.BOTH;
constraints.weightx = 1;
constraints.weightx = 1;
panel.add(label, constraints);
return panel;
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
final JPanel panel = createPanel();
panel.setSize(panel.getPreferredSize());
panel.doLayout();
System.err.println(label.getSize() + "");
// JFrame frame = new JFrame();
// frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// frame.getContentPane().add(panel);
// frame.pack();
// frame.setVisible(true);
final BufferedImage image = new BufferedImage(panel.getBounds().width, panel.getBounds().height,
BufferedImage.TYPE_INT_ARGB);
final Graphics2D gc = image.createGraphics();
gc.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
gc.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
try {
panel.paint(gc);
File output = new File("image.png");
ImageIO.write(image, "png", output);
Desktop.getDesktop().open(output);
} catch (IOException e) {
e.printStackTrace();
} finally {
gc.dispose();
}
}
});
}
}
答案 2 :(得分:1)
在您尝试打印/绘制的根组件上调用addNotify
也可以解决问题。这个问题的症结似乎是验证要求短路,除非容器有“同行”。调用addNotify
初始化对等体,允许对Component.validate
的后续调用按照正常情况在非无头情况下运行。
将此作为备用解决方案提交,因为doLayout
由于doLayout
未布置子组件而导致组件嵌套更深,因此调用DataTable dt = new DataTable();
foreach (DataRow dataRow in dt.Rows)
{
var value = dataRow.Field<int>("ColumnName");
}
将不起作用。 (虽然camickr的答案中提到的ScreenImage类通过递归调用doLayout来解决这个问题。)
答案 3 :(得分:0)
尝试使用其中一个构造函数来实例化BufferedImage
。 GraphicsEnvironment
类可能必须与真正的GraphicsEnvironment(屏幕,...)
new BufferedImage(panel.getBounds().width, panel.getBounds().height, BufferedImage.TYPE_INT_ARGB )