【发布时间】:2011-11-14 06:00:59
【问题描述】:
当 OP 请求代码示例时,我正在提供有关在 Java API or Tool to convert tabular data into PNG image file 上捕获表格数据图像的建议。结果比我想象的要难! JTable 标头从代码写入的 PNG 中消失。
PNG
屏幕截图
import javax.swing.*;
import java.awt.Graphics;
import java.awt.BorderLayout;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.io.File;
class TableImage {
public static void main(String[] args) throws Exception {
Object[][] data = {
{"Hari", new Integer(23), new Double(78.23), new Boolean(true)},
{"James", new Integer(23), new Double(47.64), new Boolean(false)},
{"Sally", new Integer(22), new Double(84.81), new Boolean(true)}
};
String[] columns = {"Name", "Age", "GPA", "Pass"};
JTable table = new JTable(data, columns);
JScrollPane scroll = new JScrollPane(table);
JPanel p = new JPanel(new BorderLayout());
p.add(scroll,BorderLayout.CENTER);
JOptionPane.showMessageDialog(null, p);
BufferedImage bi = new BufferedImage(
(int)p.getSize().getWidth(),
(int)p.getSize().getHeight(),
BufferedImage.TYPE_INT_RGB
);
Graphics g = bi.createGraphics();
p.paint(g);
JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(bi)));
ImageIO.write(bi,"png",new File("table.png"));
}
}
注意:我检查了 camickr 的 Screen Image 类并包含对 doLayout(Component) 方法的调用。如果 Component 从未在屏幕上实现,但对这段代码没有影响(在尝试呈现之前会在选项窗格中弹出包含表格的面板),则该方法很有用。
为了让表头呈现需要什么?
更新 1
换行..
p.paint(g);
..to(带有适当的导入)..
p.paint(g);
JTableHeader h = table.getTableHeader();
h.paint(g);
..产生..
我会继续调整它。
更新 2
kleopatra (strategy 1) 和 camickr (strategy 2) 都提供了答案,两者都有效,并且都不需要将 JTable 添加到虚拟组件(这是一个巨大的 hack IMO)。
虽然策略 2 将裁剪(或扩展)为“仅表格”,第一个策略将捕获包含表格的面板。如果表格包含许多条目,这将成为问题,显示带有滚动条的截断表格的图像。
虽然策略 1 可能会进一步调整以解决这个问题,我真的很喜欢策略 2 的简洁明了,所以它很受欢迎。
正如 kleopatra 所指出的,不需要“调整”。所以我会再试一次..
更新 3
这是由camickr和kleopatra提出的方法产生的图像。我会放两次,但在我看来,它们是相同的(尽管我没有逐个像素进行比较)。
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import java.io.File;
class TableImage {
String[] columns = {"Name", "Age", "GPA", "Pass"};
/** Any resemblance to persons living or dead is purely incidental. */
Object[][] data = {
{"André", new Integer(23), new Double(47.64), new Boolean(false)},
{"Jeanie", new Integer(23), new Double(84.81), new Boolean(true)},
{"Roberto", new Integer(22), new Double(78.23), new Boolean(true)}
};
TableImage() {
}
public JTable getTable() {
JTable table = new JTable(data, columns);
table.setGridColor(new Color(115,52,158));
table.setRowMargin(5);
table.setShowGrid(true);
return table;
}
/** Method courtesy of camickr.
https://stackoverflow.com/questions/7369814/why-does-the-jtable-header-not-appear-in-the-image/7375655#7375655
Requires ScreenImage class available from..
http://tips4java.wordpress.com/2008/10/13/screen-image/ */
public BufferedImage getImage1(JTable table) {
JScrollPane scroll = new JScrollPane(table);
scroll.setColumnHeaderView(table.getTableHeader());
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JPanel p = new JPanel(new BorderLayout());
p.add(scroll, BorderLayout.CENTER);
BufferedImage bi = ScreenImage.createImage(p);
return bi;
}
/** Method courtesy of kleopatra.
https://stackoverflow.com/questions/7369814/why-does-the-jtable-header-not-appear-in-the-image/7372045#7372045 */
public BufferedImage getImage2(JTable table) {
JScrollPane scroll = new JScrollPane(table);
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JPanel p = new JPanel(new BorderLayout());
p.add(scroll, BorderLayout.CENTER);
// without having been shown, fake a all-ready
p.addNotify();
// manually size to pref
p.setSize(p.getPreferredSize());
// validate to force recursive doLayout of children
p.validate();
BufferedImage bi = new BufferedImage(p.getWidth(), p.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics g = bi.createGraphics();
p.paint(g);
g.dispose();
return bi;
}
public void writeImage(BufferedImage image, String name) throws Exception {
ImageIO.write(image,"png",new File(name + ".png"));
}
public static void main(String[] args) throws Exception {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
TableImage ti = new TableImage();
JTable table;
BufferedImage bi;
table = ti.getTable();
bi = ti.getImage1(table);
ti.writeImage(bi, "1");
JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(bi)));
table = ti.getTable();
bi = ti.getImage2(table);
ti.writeImage(bi, "2");
JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(bi)));
}
}
两者都实现了目标。使用 camickr 的方法,您可以利用 ScreenImage API 的更多功能。使用 kleopatra 的方法 - 纯 J2SE 大约有十几行(减去 cmets 和空白)。
虽然 ScreenImage 是我将来会使用和推荐的一个类,但使用核心 J2SE 的另一种方法是我可能会在这种确切情况下使用的方法。
因此,虽然 'tick' 将留在 camickr,但赏金将留在 kleopatra。
【问题讨论】:
-
这样的问题让我不敢使用 Swing。
-
@Kublai 这是一个奇怪的角落案例,你是一只害怕的猫。 ;) BTW - 尝试在 AWT 中进行。
-
“哥们,我的头在哪里?” - 哥们,我不知道!你最后把它放在哪里了? :-)
-
@Stephen 这是电影“老兄!我的车在哪里”的双关语:imdb.com/title/tt0242423
-
@Suraj 也许这是斯蒂芬的一个挖洞,大意是我不应该在做双关语。很高兴我没有使用被认为是“老兄,我的头在哪里?”的变体。 ;)