为什么JTable标题不出现在图像中?

我提供了关于捕获Java API或工具上的表格数据的图像的build议, 以将表格数据转换成PNG图像文件 – 当OP请求代码示例时。 结果比我想象的要难! JTable头从代码写入的PNG中消失。

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在屏幕上从未实现过,但对此代码没有影响(在试图呈现它之前popup包含在选项窗格中的表的面板)是有用的。

需要什么来获取表头来呈现?

更新1

改变线路

  p.paint(g); 

..(与适当的import)..

  p.paint(g); JTableHeader h = table.getTableHeader(); h.paint(g); 

..produces ..

第二次尝试

我会继续调整它。

更新2

kleopatra(策略1)和camickr(策略2)每个都提供了一个答案,两者都工作,这两者都不需要将JTable添加到虚拟组件(这是一个巨大的黑客IMO)。

虽然战略2将收获(或扩大)“只是表”, 第一个策略将捕获包含该表格的面板。 如果表中包含许多条目,则会出现问题,并显示带有滚动条的截断表的图像。

虽然战略1可能会进一步调整来解决这个问题, 我真的很喜欢战略2的简洁,所以得到了答案。

正如kleopatra所指出的那样,没有必要进行“调整”。 所以我会再试一次

更新3

这是camickr和kleopatra提出的方法所产生的图像。 我已经把它两次,但在我看来,他们是相同的(虽然我没有做一个像素逐像素比较)。

在Nimbus PLAF中的JTable

 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的更多function。 使用kleopatra的方法 – 纯J2SE的十几行(less于注释和空白)。

虽然ScreenImage是我将来使用和推荐的一个类,但使用核心J2SE的另一种方法是我可能用于这种确切的情况。

所以,当“嘀嗒”将留在camickr,赏金将要kleopatra。

这个线程并不需要先渲染表格,但这是最终的目标

ScreenImage处理这个。

您必须手动将标题添加到滚动窗格。

 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); scroll.setColumnHeaderView(table.getTableHeader()); table.setPreferredScrollableViewportSize(table.getPreferredSize()); JPanel p = new JPanel(new BorderLayout()); p.add(scroll, BorderLayout.CENTER); BufferedImage bi = ScreenImage.createImage(p); JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(bi))); ImageIO.write(bi,"png",new File("table.png")); } } 

注意:Kleopatrabuild议在面板上使用addNotify()不适用于ScreenImage。 addNotify()方法使组件displayable ,并且ScreenImage代码将只展示不可显示组件的组件。 我可能会考虑使这个更一般。

这是一个艰难的

迷惑不解的是,这个标题没有出现在图像上:它没有被剪掉或者什么东西,根本没有被绘出。 原因在于,在将面板绘制到图像时,标题不再是层次结构的一部分。 当closuresoptionPane时,table.removeNotify删除标题。 要再次添加,请调用addNotify,如下面的代码片段所示:

  JScrollPane scroll = new JScrollPane(table); JPanel p = new JPanel(new BorderLayout()); p.add(scroll, BorderLayout.CENTER); JOptionPane.showMessageDialog(null, p); table.addNotify(); p.doLayout(); BufferedImage bi = new BufferedImage(p.getWidth() + 100, p.getHeight() + 100, BufferedImage.TYPE_INT_RGB); Graphics g = bi.createGraphics(); p.paint(g); g.dispose(); 

[编辑解决]我还是不明白,为什么面板显示为空,而没有显示在optionPane – 通常是一些组合..

  p.doLayout(); p.setSize(p.getPreferredSize()) 

… 会做

编辑

解决了最后的困惑:为了强制对窗格进行recursion重新布局,必须将所有沿着容器的容器驱动成相信它们具有对等体 – 如果不是,则优化为不做任何事情。 做面板上的addNotify(而不是在桌子上)再加上validate就可以了

  // JOptionPane.showMessageDialog(null, p); // 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() + 100, p.getHeight() + 100, BufferedImage.TYPE_INT_RGB); Graphics g = bi.createGraphics(); p.paint(g); g.dispose(); JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(bi))); 

没有testing副作用。

编辑2

只是抱怨一下..

战略1可能会进一步调整,以避开[截断表列]

实际上并不需要调整(或者需要与ScreenImage相同的调整,取决于你认为的一个调整:) – 两者都需要通过设置表的prefViewportSize来完成它的工作,两者完全相同:

  // strategy 1 table.setPreferredScrollableViewportSize(table.getPreferredSize()); p.addNotify(); // strategy 2 scroll.setColumnHeaderView(table.getTableHeader()); table.setPreferredScrollableViewportSize(table.getPreferredSize()); 

手动将您的JPanel添加到您自己的创build的JFrame ,而不仅仅是JOptionPane使用的JFrame似乎可以解决这个问题。

如果需要,介绍JFrame可以跳过最初的对话框显示。

 // ...snip JOptionPane.showMessageDialog(null, p); JFrame frame = new JFrame(); frame.setContentPane(p); frame.pack(); BufferedImage bi = new BufferedImage( (int)p.getSize().getWidth(), (int)p.getSize().getHeight(), BufferedImage.TYPE_INT_RGB ); Graphics g = bi.createGraphics(); p.paint(g); g.dispose(); frame.dispose(); JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(bi))); ImageIO.write(bi,"png",new File("table.png")); 

表格图像

 import javax.swing.*; import javax.swing.table.JTableHeader; 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); JTableHeader h = table.getTableHeader(); int x = h.getWidth(); int y = h.getHeight() + table.getHeight(); BufferedImage bi = new BufferedImage( (int)x, (int)y, BufferedImage.TYPE_INT_RGB ); Graphics g = bi.createGraphics(); h.paint(g); g.translate(0,h.getHeight()); table.paint(g); JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(bi))); ImageIO.write(bi,"png",new File("table.png")); } } 

请,这不是答案只是评论和一点点…. 🙂

@Andrew Thompson,如果我正确的理解了例如 ,只是参考J/Components是在Graphics2D g2 = (Graphics2D) g;里面/过去Graphics2D g2 = (Graphics2D) g; 并没有接受引用/派生TableHeader ....可能(我懒得检查)在API中的东西

代码剪切显示:-)

 g2.scale(scale,scale); tableView.paint(g2); g2.scale(1/scale,1/scale); g2.translate(0f,pageIndex*pageHeightForTable); g2.translate(0f, -headerHeightOnPage); g2.setClip(0, 0, (int) Math.ceil(tableWidthOnPage), (int)Math.ceil(headerHeightOnPage)); g2.scale(scale,scale); tableView.getTableHeader().paint(g2); //paint header at top 

在这里输入图像描述

 import javax.swing.*; import javax.swing.table.JTableHeader; import java.awt.Graphics; import java.awt.BorderLayout; import java.awt.Graphics2D; 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), (true)}, {"James", new Integer(23), new Double(47.64), (false)}, {"Sally", new Integer(22), new Double(84.81), (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); JTableHeader h = table.getTableHeader(); int x = h.getWidth(); int y = h.getHeight() + table.getHeight(); BufferedImage bi = new BufferedImage( x, y, BufferedImage.TYPE_INT_RGB); Graphics g = bi.createGraphics(); //g.translate(0, table.getTableHeader().getHeight()); Graphics2D g2 = (Graphics2D) g; table.paint(g2); //table.paint(g); table.getTableHeader().paint(g2); //h.paint(g); JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(bi))); ImageIO.write(bi, "png", new File("ctable.png")); } private TableImage() { } } 

尝试在打印组件(或全局)之前禁用双缓冲( c.setDoubleBuffered(false) )。 显然它有一个效果:)(见http://www.java2s.com/Code/Java/2D-Graphics-GUI/PrintSwingcomponents.htm和http://www.apl.jhu.edu/~hall/java/ Swing-Tutorial / Swing-Tutorial-Printing.html )