Java 为什么JTable标头不出现在图像中?
当OP请求一个代码示例时,我正在提供关于在上捕获表格数据图像的建议。结果比我想象的要难!Java 为什么JTable标头不出现在图像中?,java,image,swing,jtable,Java,Image,Swing,Jtable,当OP请求一个代码示例时,我正在提供关于在上捕获表格数据图像的建议。结果比我想象的要难!JTable头从代码编写的PNG中消失 巴布亚新几内亚 截屏 注意:我检查了camickr的类,包括对doLayout(Component)方法的调用。如果组件从未在屏幕上实现,但对该代码没有影响(在尝试渲染之前,会在选项窗格中弹出包含该表的面板),则该方法非常有用 要获得要呈现的表头,需要什么 更新1 换行 p.paint(g); …到(具有适当的导入) …生产 我会继续调整它 更新
JTable
头从代码编写的PNG中消失
巴布亚新几内亚
截屏
注意:我检查了camickr的类,包括对doLayout(Component)
方法的调用。如果组件
从未在屏幕上实现,但对该代码没有影响(在尝试渲染之前,会在选项窗格中弹出包含该表的面板),则该方法非常有用
要获得要呈现的表头,需要什么
更新1
换行
p.paint(g);
…到(具有适当的导入)
…生产
我会继续调整它
更新2
kleopatra(策略1)和camickr(策略2)分别提供了一个答案,这两个答案都有效,并且都不需要将JTable
添加到虚拟组件(这是一个巨大的漏洞)
虽然策略2将裁剪(或扩展)为“仅表格”,但第一个策略将捕获包含表格的面板。如果表格包含许多条目,显示带有滚动条的截断表格的图像,则会出现问题
虽然策略1可能会被进一步调整以避开这个问题,但我真的很喜欢策略2简洁的简洁性,因此它得到了支持
正如克利奥帕特拉所指出的,不需要“调整”。所以我会再试一次
更新3
这是由camickr和kleopatra提出的方法生成的图像。我会放两次,但在我看来,它们是相同的(尽管我没有做逐像素比较)
两者都实现了目标。使用camickr的方法,您可以进一步利用ScreenImage API的强大功能。使用kleopatra的方法-大约十几行纯J2SE代码(减去注释和空白)
虽然ScreenImage是我将来将使用和推荐的一个类,但另一种使用核心J2SE的方法可能就是我在这种情况下可能使用的方法
因此,虽然“勾号”将保留在camickr上,但赏金将转移到kleopatra。在打印组件之前尝试禁用组件(或全局)上的双缓冲(
c.setDoubleBuffered(false)
)。显然它有一种效果:)(见和)
请,这不是答案,只是评论和一点点…:-) @安德鲁·汤普森,如果我正确理解的话,只需参考
J/Components
是in/passGraphics2D g2=(Graphics2D)g代码>和未被接受引用/派生的TableHeader….
可能(我懒得检查)API中的某些内容
代码截取显示:-)
手动将您的JPanel
添加到您自己创建的JFrame
,而不仅仅是JOptionPane使用的,似乎可以解决这个问题
如果需要,引入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"));
那是一个艰难的时刻
令人困惑的是,头球根本没有出现在图片上:它没有被剪掉或什么的,它根本没有被画出来。原因是。。在将面板绘制到图像时,标题不再是层次的一部分。关闭选项窗格时,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();
[在编辑中解决]我仍然不明白为什么面板显示为空,而没有首先显示在选项窗格中-通常是一些组合
p.doLayout();
p.setSize(p.getPreferredSize())
。。。行
编辑
最后解决的困惑是:为了强制递归地重新布局窗格,必须让所有容器相信它们有一个对等验证,否则将优化为什么也不做。在面板上(而不是在表上)执行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)));
没有测试副作用
编辑2
只是有点抱怨
策略1可能会进一步调整,以绕过[截断的表列]
实际上不需要TWIWE(或者对于ScreenImage需要的相同的调整,取决于您考虑的一个调整:)-两个都需要通过设置表的PrfVIEWTRAWSIZE使JScRunPANT不做它的工作,在两个完全相同的行:
// strategy 1
table.setPreferredScrollableViewportSize(table.getPreferredSize());
p.addNotify();
// strategy 2
scroll.setColumnHeaderView(table.getTableHeader());
table.setPreferredScrollableViewportSize(table.getPreferredSize());
该线程并不要求在不显示表的情况下呈现表,但这是最终目标
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"));
}
}
注意:Kleopatra建议在面板上使用addNotify()将不适用于ScreenImage。addNotify()方法使组件可显示
,而ScreenImage代码将仅为不可显示的组件布局组件。我可能会考虑让这更一般化。像这样的问题让我不敢使用Swing。@忽必烈这是一个奇怪的角落案例,你是一只胆小鬼。。)顺便说一句-试试AWT。“老兄,我的头球呢?”-老兄,我不知道!你上次把它放在哪里了?:-)@斯蒂芬:这是电影《老兄!我的车在哪里》中的双关语:@Suraj也许这是斯蒂芬的挖苦,大意是我不应该用双关语。很高兴我没有使用被认为是“老兄,我的头在哪里?”)的变体调用c.setDoubleBuffered(false)
没有明显的效果。也许这纯粹是与印刷有关。这一点很好。这个帖子并不要求你在不显示表格的情况下呈现表格,但这是最终目标。我删除了我的帖子,请修改你的自我回答。我希望你不要删除你的答案。但是你确定它有效吗?在我的例子中,我无法让它工作-也许我只是用错了。天哪,老兄,确保它工作,:-)我肯定你在某个地方丢失了:-),我看不到自定义打印和自定义绘制之间的差异,从JTable plus Ta获取大小
// ...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"));
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();
p.doLayout();
p.setSize(p.getPreferredSize())
// 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)));
// strategy 1
table.setPreferredScrollableViewportSize(table.getPreferredSize());
p.addNotify();
// strategy 2
scroll.setColumnHeaderView(table.getTableHeader());
table.setPreferredScrollableViewportSize(table.getPreferredSize());
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"));
}
}