带有多行单元格渲染器的 JTable 打印非常奇怪
我有一个带有用于多行单元格的自定义单元格渲染器的 JTable。一切都很好,JTable 在屏幕上画得很好,我对此非常满意,但是昨晚当我尝试简单地打印它时,我遇到了一个非常奇怪的问题。使用:
table.print(PrintMode.FIT_WIDTH, new MessageFormat("..."), new MessageFormat("..."));
我看到表格没有完全打印出来。然后使用同事制作的另一个类来打印 JTables,我得到了相同的结果:
该表(带有多行单元格)需要 22 页来打印。打印的文档(我只以 xps 格式查看,因为我没有打印机)也有 22 页。但直到第 16 页,所有内容都按预期打印,此后仅打印表格的边框和列标题。
奇怪的是(对我来说),当我尝试使用另一个不允许多行单元格的单元格渲染器打印表格时,该表格恰好需要 16 页并且被完全打印,尽管在冗长的单元格值中进行了裁剪。
我在整个网络上进行了搜索,但没有运气。有谁知道为什么会发生这种情况?有解决办法吗?
更新:
我的单元格渲染器如下:
public class MultiLineTableCellRenderer extends JTextPane implements TableCellRenderer {
private List<List<Integer>> rowColHeight = new ArrayList<List<Integer>>();
public MultiLineTableCellRenderer() {
setOpaque(true);
}
public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
String s = (String)value;
if (s.equals("<περιοδάριθμος>")) {
setForeground(Color.blue);
}
else if(s.equals("<παραγραφάριθμος>")) {
setForeground(Color.red);
}
else {
setForeground(Color.black);
}
setBackground(new Color(224, 255, 255));
if (isSelected) {
setBackground(Color.GREEN);
}
setFont(table.getFont());
setFont(new Font("Tahoma", Font.PLAIN, 10));
if (hasFocus) {
setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
if (table.isCellEditable(row, column)) {
setForeground(UIManager.getColor("Table.focusCellForeground"));
setBackground(UIManager.getColor("Table.focusCellBackground"));
}
} else {
setBorder(new EmptyBorder(1, 2, 1, 2));
}
if (value != null) {
setText(value.toString());
} else {
setText("");
}
adjustRowHeight(table, row, column);
SimpleAttributeSet bSet = new SimpleAttributeSet();
StyleConstants.setAlignment(bSet, StyleConstants.ALIGN_CENTER);
StyleConstants.setFontFamily(bSet, "Tahoma");
StyleConstants.setFontSize(bSet, 11);
StyledDocument doc = getStyledDocument();
doc.setParagraphAttributes(0, 100, bSet, true);
return this;
}
private void adjustRowHeight(JTable table, int row, int column) {
int cWidth = table.getTableHeader().getColumnModel().getColumn(column).getWidth();
setSize(new Dimension(cWidth, 1000));
int prefH = getPreferredSize().height;
while (rowColHeight.size() <= row) {
rowColHeight.add(new ArrayList<Integer>(column));
}
List<Integer> colHeights = rowColHeight.get(row);
while (colHeights.size() <= column) {
colHeights.add(0);
}
colHeights.set(column, prefH);
int maxH = prefH;
for (Integer colHeight : colHeights) {
if (colHeight > maxH) {
maxH = colHeight;
}
}
if (table.getRowHeight(row) != maxH) {
table.setRowHeight(row, maxH);
}
}
}
此外,如果您测试以下非常简单的示例,您会注意到打印有严重错误,但我真的找不到什么!
public static void main(String[] args) throws PrinterException {
DefaultTableModel model = new DefaultTableModel();
model.addColumn("col1");
model.addColumn("col2");
model.addColumn("col3");
int i = 0;
for (i = 1; i <= 400; i++) {
String a = "" + i;
model.addRow(new Object[]{a, "2", "3"});
}
JTable tab = new JTable(model);
tab.print();
}
I have a JTable with a custom Cell Renderer for multi-line cells. Everything is ok, the JTable is painted ok in the screen and I am very happy with it, but ast night when I tried to simply print it, I came up with a very strange issue. Using:
table.print(PrintMode.FIT_WIDTH, new MessageFormat("..."), new MessageFormat("..."));
I saw that the table did not print entirely. Then using another class made from a colleague for printing JTables I had the same result:
The table (with multi-line cells) needed 22 pages to print. The printed document (which I only viewed in xps format since I do not own a printer) had also 22 pages. But up to page 16 everything was printed as expected and after that only the borders and the column headers of the table were printed.
Strangely (to me) enough, when I tried to print the table using another cell renderer that does not allow for multi line cells, the table needed exactly 16 pages and was printed entirely, albeit the cropping in the lengthy cell values.
I searched all over the net but I had no luck. Does anybody know why could this be happening? Is there a solution?
Update:
My cell renderer is the following:
public class MultiLineTableCellRenderer extends JTextPane implements TableCellRenderer {
private List<List<Integer>> rowColHeight = new ArrayList<List<Integer>>();
public MultiLineTableCellRenderer() {
setOpaque(true);
}
public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
String s = (String)value;
if (s.equals("<περιοδάριθμος>")) {
setForeground(Color.blue);
}
else if(s.equals("<παραγραφάριθμος>")) {
setForeground(Color.red);
}
else {
setForeground(Color.black);
}
setBackground(new Color(224, 255, 255));
if (isSelected) {
setBackground(Color.GREEN);
}
setFont(table.getFont());
setFont(new Font("Tahoma", Font.PLAIN, 10));
if (hasFocus) {
setBorder(UIManager.getBorder("Table.focusCellHighlightBorder"));
if (table.isCellEditable(row, column)) {
setForeground(UIManager.getColor("Table.focusCellForeground"));
setBackground(UIManager.getColor("Table.focusCellBackground"));
}
} else {
setBorder(new EmptyBorder(1, 2, 1, 2));
}
if (value != null) {
setText(value.toString());
} else {
setText("");
}
adjustRowHeight(table, row, column);
SimpleAttributeSet bSet = new SimpleAttributeSet();
StyleConstants.setAlignment(bSet, StyleConstants.ALIGN_CENTER);
StyleConstants.setFontFamily(bSet, "Tahoma");
StyleConstants.setFontSize(bSet, 11);
StyledDocument doc = getStyledDocument();
doc.setParagraphAttributes(0, 100, bSet, true);
return this;
}
private void adjustRowHeight(JTable table, int row, int column) {
int cWidth = table.getTableHeader().getColumnModel().getColumn(column).getWidth();
setSize(new Dimension(cWidth, 1000));
int prefH = getPreferredSize().height;
while (rowColHeight.size() <= row) {
rowColHeight.add(new ArrayList<Integer>(column));
}
List<Integer> colHeights = rowColHeight.get(row);
while (colHeights.size() <= column) {
colHeights.add(0);
}
colHeights.set(column, prefH);
int maxH = prefH;
for (Integer colHeight : colHeights) {
if (colHeight > maxH) {
maxH = colHeight;
}
}
if (table.getRowHeight(row) != maxH) {
table.setRowHeight(row, maxH);
}
}
}
Furthermore, if you test the following very simple example you will notice that something is terribly wrong with the printing, but I really can't find what!
public static void main(String[] args) throws PrinterException {
DefaultTableModel model = new DefaultTableModel();
model.addColumn("col1");
model.addColumn("col2");
model.addColumn("col3");
int i = 0;
for (i = 1; i <= 400; i++) {
String a = "" + i;
model.addRow(new Object[]{a, "2", "3"});
}
JTable tab = new JTable(model);
tab.print();
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我相信您遇到的问题与我问这个问题时遇到的问题相同:
截断的 JTable 打印输出
我找到了解决问题的方法,我相信它也会对您有所帮助。
答案在这里:
截断的 JTable 打印输出
总结我的答案:
如果您的 TableCellRenderer 是唯一的地方在您将行设置为正确高度的代码中,您将遇到由 JTable 内部优化引起的麻烦:JTable 只为已经(或即将)显示的单元格调用 TableCellRenderers。
如果并非所有单元格都已显示在屏幕上,则并非所有渲染器都已被调用,因此并非所有行都已设置为所需的高度。由于行的高度不正确,因此 JTable 的整体高度不正确。毕竟,确定 JTable 整体高度的一部分是考虑该表的每一行的高度。如果 JTable 总高度不正确,则会导致打印截断,因为 JTable 总高度是打印布局逻辑中考虑的参数。
解决此问题的一个简单(但可能不是很干净)的方法是在打印之前手动访问所有单元格渲染器。有关执行此操作的示例,请参阅我的链接答案。实际上,我选择在用数据填充表后立即进行渲染器访问,因为这修复了 JTable 滚动条范围的一些错误行为(除了修复打印之外)。
即使在打印时,表在屏幕上看起来和工作正常的原因被破坏了,是因为当您在表格中滚动时,当屏幕上出现新单元格时,会调用各种渲染器,并且渲染器为新可见的行设置适当的行高,然后动态重新计算各种尺寸,当您与桌子互动时,最终一切都会顺利进行。 (尽管您可能会注意到滚动条“extent”随着您滚动而改变,这通常不应该发生。)
I believe you are having the same problem that I had when I asked this question:
Truncated JTable print output
I found a solution to my problem, and I believe it may help you as well.
The answer is here:
Truncated JTable print output
To summarize my answer:
If your TableCellRenderer is the only place in your code where you are setting rows to their correct height, then you are going to run into trouble caused by an optimization inside JTable: JTable only invokes TableCellRenderers for cells that have been (or are about to be) displayed.
If not all of your cells have been displayed on-screen, then not all of your renderers have been invoked, and so not all of your rows have been set to the desired height. With your rows not being their correct height, your JTable overall height is incorrect. After all, part of determining the overall JTable height is accounting for the height of each of that table's rows. If the JTable overall height isn't correct, this causes the print to truncate, since the JTable overall height is a parameter that is considered in the print layout logic.
An easy (but perhaps not squeaky clean) way to fix this is to visit all of your cell renderers manually before printing. See my linked answer for an example of doing this. I actually chose to do the renderer visitation immediately after populating my table with data, because this fixes some buggy behavior with the JTable's scrollbar extents (in addition to fixing the printing.)
The reason the table looks and works OK on-screen even when printing is broken, is because as you scroll around in the table, the various renderers are invoked as new cells come on screen, and the renderers set the appropriate row height for the newly visible rows, and various dimensions are then are recalculated on the fly, and everything works out OK in the end as you interact with the table. (Although you may notice that the scrollbar "extent" changes as you scroll around, which it really shouldn't normally do.)
这种行为总是让我怀疑同步不正确。
目前尚不清楚您的
TableCellRenderer
是如何工作的,但您可以尝试 HTML,许多 Swing 组件都支持它。另一个有用的练习是准备一个sscce来重现问题的缩影。一个小而完整的例子可能会暴露这个问题。它还允许其他人在不同平台上测试您的方法。
Such behavior always makes me suspect incorrect synchronization.
It's not clear how your
TableCellRenderer
works, but you might try HTML, which is supported in many Swing components.Another useful exercise is to prepare an sscce that reproduces the problem in minature. A small, complete example might expose the problem. It would also allow others to test your approach on different platforms.
对于提出这个问题的人来说,这个答案可能为时已晚,但对于遇到类似问题的每个人来说,这是我的解决方案;
我遇到了完全相同的问题,我有自己的 TableCellRenderer 来处理多行字符串,它可以完美地显示表格,但使表格的打印不可靠。
我的解决方案由两部分组成;
第 1 部分:我创建了自己的 TableModel,在 getValueAt() 中,我“复制”了 StringCellRenderer 逻辑的一部分,使其重新计算并设置表格行的高度,以防出现多行字符串并返回字符串作为带有“breaks”而不是行分隔符的 HTML。
第 2 部分:在调用 table.print() 之前,我为所有单元格调用 getValueAt() (对列进行 for 循环,对调用 getValueAt() 的行进行内部 for 循环),这必须“手动”完成' 因为打印功能不会调用所有 getValueAt(我在不同的论坛上找到了有关 TableCellRenderers 执行问题的原因)。
这样,表格的裁剪就像预期的那样完成,每页只打印完整的行,并且如果需要,每页都有表格标题,它会将行划分为几页。
This answer is probably too late for the one who asked this question, but for everybody with a similar problem, here is my solution;
I had exactly the same problem, I have my own TableCellRenderer to handle multi-line Strings which works flawless for showing the table but makes the printing of the table unreliable.
My solutions consists of 2 parts;
Part 1: I have created my own TableModel, in the getValueAt() I 'copied' a part of the StringCellRenderer logic, I make it recalculate and set the height of the table row in case af a multi-line String AND return the String as HTML with 'breaks' instead of line-separators.
Part 2: Before invoking the table.print() I call the getValueAt() for all cells (a for-loop over the columns with an inner for loop over the rows invoking the getValueAt()), this has to be done 'manually' because the print functionality doesn't invoke all getValueAt's (I have found reasons on different fora regarding this issue regarding the execution of the TableCellRenderers).
This way the clipping of the table is done like it is supposed to, only complete rows are printed per page and it devides the rows over severall pages if required with a table header at each page.