截断的 JTable 打印输出

发布于 2024-10-15 12:31:49 字数 4708 浏览 2 评论 0原文

我有一个 JTable,它使用 JTextArea 作为其 TableCellRenderer,以便表格单元格可以利用自动换行。 JTable 显示正常。当我通过 JTable 的 打印方法,输出总是在大约60%的数据处被截断。我尝试过不同的计算机、不同的打印机、不同的打印机驱动程序、不同的 JVM 版本(1.5、1.6),但都没有帮助。下面是一个独立的 Java 主类,它重现了该问题。有什么想法吗?

import java.awt.*;
import java.awt.event.*;
import java.awt.print.*;
import java.util.*;
import javax.swing.*;
import javax.swing.table.*;

public class JTextAreaJTableTest extends javax.swing.JFrame {

    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                JTextAreaJTableTest frame = new JTextAreaJTableTest();
                frame.setSize(640, 480);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    JButton jButtonPrint;
    JScrollPane jScrollPane;
    JTable jTable;
    JToolBar jToolBar;

    public JTextAreaJTableTest() {
        initComponents();

        DefaultTableModel dtm = (DefaultTableModel) jTable.getModel();
        Vector<Vector<String>> data = new Vector<Vector<String>>();
        for (int i = 0; i < 50; i++) {
            Vector<String> rowData = new Vector<String>();
            rowData.add("Entry " + i);
            rowData.add("Lorem ipsum dolor sit amet, consectetur adipisicing"
                    + " elit, sed do eiusmod tempor incididunt ut labore et"
                    + " dolore magna aliqua. Ut enim ad minim veniam, quis"
                    + " nostrud exercitation ullamco laboris nisi ut aliquip"
                    + " ex ea commodo consequat. Duis aute irure dolor in"
                    + " reprehenderit in voluptate velit esse cillum dolore"
                    + " eu fugiat nulla pariatur. Excepteur sint occaecat"
                    + " cupidatat non proident, sunt in culpa qui officia"
                    + " deserunt mollit anim id est laborum. " + i);
            data.add(rowData);
        }
        Vector<String> columnNames = new Vector<String>();
        columnNames.add("Key");
        columnNames.add("Value");
        dtm.setDataVector(data, columnNames);
        jTable.setDefaultRenderer(String.class, null);
        jTable.getColumnModel().getColumn(0).setCellRenderer(
                new TextAreaCellRenderer());
        jTable.getColumnModel().getColumn(1).setCellRenderer(
                new TextAreaCellRenderer());
    }

    private void initComponents() {
        jToolBar = new JToolBar();
        jButtonPrint = new JButton();
        jScrollPane = new JScrollPane();
        jTable = new JTable();

        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        jToolBar.setRollover(true);

        jButtonPrint.setText("Print");
        jButtonPrint.setFocusable(false);
        jButtonPrint.setHorizontalTextPosition(SwingConstants.CENTER);
        jButtonPrint.setVerticalTextPosition(SwingConstants.BOTTOM);
        jButtonPrint.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                jButtonPrintActionPerformed(evt);
            }
        });
        jToolBar.add(jButtonPrint);

        getContentPane().add(jToolBar, BorderLayout.NORTH);

        jScrollPane.setViewportView(jTable);

        getContentPane().add(jScrollPane, BorderLayout.CENTER);
    }

    private void jButtonPrintActionPerformed(ActionEvent evt)                                             
    {                                                 
        try {
            jTable.print();
        } catch (PrinterException ex) {
            ex.printStackTrace();
        }
    }                                            

    public static class TextAreaCellRenderer extends JTextArea implements
            TableCellRenderer {

        public TextAreaCellRenderer() {
            this.setLineWrap(true);
            this.setWrapStyleWord(true);
        }

        public Component getTableCellRendererComponent(JTable table, 
                Object value, boolean isSelected, boolean hasFocus,
                int row, int column) {
            this.setText(String.valueOf(value));
            TableColumnModel columnModel = table.getColumnModel();
            this.setSize(columnModel.getColumn(column).getWidth(), 1);
            int newHeight = this.getPreferredSize().height;
            if (newHeight > table.getRowHeight(row)) {
                table.setRowHeight(row, this.getPreferredSize().height);
            }
            return this;
        }
    }
}

I have a JTable that uses JTextArea as its TableCellRenderer, so that table cells can utilize word wrap. The JTable displays fine. When I print the table to a printer via JTable's print method, the output is always truncated at approximately 60% of the data. I have tried different computers and different printers, and different printer drivers, different JVM versions (1.5, 1.6) but none of that has helped. Below is a self-contained Java main class that reproduces the problem. Any ideas?

import java.awt.*;
import java.awt.event.*;
import java.awt.print.*;
import java.util.*;
import javax.swing.*;
import javax.swing.table.*;

public class JTextAreaJTableTest extends javax.swing.JFrame {

    public static void main(String args[]) {
        java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                JTextAreaJTableTest frame = new JTextAreaJTableTest();
                frame.setSize(640, 480);
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    JButton jButtonPrint;
    JScrollPane jScrollPane;
    JTable jTable;
    JToolBar jToolBar;

    public JTextAreaJTableTest() {
        initComponents();

        DefaultTableModel dtm = (DefaultTableModel) jTable.getModel();
        Vector<Vector<String>> data = new Vector<Vector<String>>();
        for (int i = 0; i < 50; i++) {
            Vector<String> rowData = new Vector<String>();
            rowData.add("Entry " + i);
            rowData.add("Lorem ipsum dolor sit amet, consectetur adipisicing"
                    + " elit, sed do eiusmod tempor incididunt ut labore et"
                    + " dolore magna aliqua. Ut enim ad minim veniam, quis"
                    + " nostrud exercitation ullamco laboris nisi ut aliquip"
                    + " ex ea commodo consequat. Duis aute irure dolor in"
                    + " reprehenderit in voluptate velit esse cillum dolore"
                    + " eu fugiat nulla pariatur. Excepteur sint occaecat"
                    + " cupidatat non proident, sunt in culpa qui officia"
                    + " deserunt mollit anim id est laborum. " + i);
            data.add(rowData);
        }
        Vector<String> columnNames = new Vector<String>();
        columnNames.add("Key");
        columnNames.add("Value");
        dtm.setDataVector(data, columnNames);
        jTable.setDefaultRenderer(String.class, null);
        jTable.getColumnModel().getColumn(0).setCellRenderer(
                new TextAreaCellRenderer());
        jTable.getColumnModel().getColumn(1).setCellRenderer(
                new TextAreaCellRenderer());
    }

    private void initComponents() {
        jToolBar = new JToolBar();
        jButtonPrint = new JButton();
        jScrollPane = new JScrollPane();
        jTable = new JTable();

        setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);

        jToolBar.setRollover(true);

        jButtonPrint.setText("Print");
        jButtonPrint.setFocusable(false);
        jButtonPrint.setHorizontalTextPosition(SwingConstants.CENTER);
        jButtonPrint.setVerticalTextPosition(SwingConstants.BOTTOM);
        jButtonPrint.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                jButtonPrintActionPerformed(evt);
            }
        });
        jToolBar.add(jButtonPrint);

        getContentPane().add(jToolBar, BorderLayout.NORTH);

        jScrollPane.setViewportView(jTable);

        getContentPane().add(jScrollPane, BorderLayout.CENTER);
    }

    private void jButtonPrintActionPerformed(ActionEvent evt)                                             
    {                                                 
        try {
            jTable.print();
        } catch (PrinterException ex) {
            ex.printStackTrace();
        }
    }                                            

    public static class TextAreaCellRenderer extends JTextArea implements
            TableCellRenderer {

        public TextAreaCellRenderer() {
            this.setLineWrap(true);
            this.setWrapStyleWord(true);
        }

        public Component getTableCellRendererComponent(JTable table, 
                Object value, boolean isSelected, boolean hasFocus,
                int row, int column) {
            this.setText(String.valueOf(value));
            TableColumnModel columnModel = table.getColumnModel();
            this.setSize(columnModel.getColumn(column).getWidth(), 1);
            int newHeight = this.getPreferredSize().height;
            if (newHeight > table.getRowHeight(row)) {
                table.setRowHeight(row, this.getPreferredSize().height);
            }
            return this;
        }
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

葬花如无物 2024-10-22 12:31:49

我相信我找到了根本原因和解决方案。打印问题源于桌子高度错误。由于表格的某些行的高度不正确,因此表格的高度不正确。某些行的高度错误,因为 JTable 尚未调用它们的单元格渲染器(即,尚未调用它们的 getTableCellRendererComponent 方法。)JTable 跳过某些单元格渲染器的原因是优化-- 它跳过的单元格渲染器适用于屏幕外的单元格 -- 屏幕外意味着不需要渲染; JTable在这方面的优化是有道理的。

然而,渲染器是程序中唯一设置每个单独行的正确高度的地方。我选择在单元格渲染器中设置行高是可以理解的,因为单元格渲染器处于了解行高度的最佳位置。

修复此示例程序的最直接方法似乎是在每个单元格渲染器上手动调用 getTableCellRendererComponent (这应该在修改表的模型数据后完成。)这使渲染器有机会设置表格中的每一行都达到其正确的单独高度。当每一行都处于正确的高度时,JTable 总体上最终会达到正确的高度,这似乎解决了打印截断问题。以下代码显示了访问所有渲染器来执行此操作:

for (int colIdx = 0; colIdx < jTable.getColumnCount(); colIdx++)
{
    for (int rowIdx = 0; rowIdx < jTable.getRowCount(); rowIdx++)
    {
        TableColumnModel columnModel = jTable.getColumnModel();
        TableColumn column = columnModel.getColumn(colIdx);
        TableCellRenderer renderer = column.getCellRenderer();
        Object cellValue = jTable.getValueAt(rowIdx, colIdx);
        renderer.getTableCellRendererComponent(jTable, cellValue, 
                    false, false, rowIdx, colIdx);
    }
}

对所有单元格渲染器的手动访问绕过了 JTable 仅调用屏幕单元格的单元格渲染器的优化。因此,您可能只想在用户请求打印之后(但在调用 JTable 上的打印方法之前)执行此解决方法代码。如果用户只是使用 GUI 而不是打印,这可以让 JTable 优化保持有效。

I believe I found the root cause and a solution. The print problem stems from the fact that the table is the wrong height. The table is the wrong height because some of the table's rows are the wrong height. Some of the rows are the wrong height because their cell renderers have not been invoked by JTable (that is, their getTableCellRendererComponent method has not been called.) The reason JTable is skipping some of the cell renderers is optimization -- the cell renderers it skips are for cells that are offscreen -- offscreen means rendering isn't needed; JTable's optimization in this area makes sense.

However, the renderers are the only place in the program where the correct height of each individual row is set. My choice to set the row height in the cell renderer is somewhat understandable because the cell renderer is in the best position to know what height the row should be.

It seems the most straight-forward way to fix this example program is to manually call getTableCellRendererComponent on every cell renderer (this should be done after modifying the table's model data.) This gives the renderers an opportunity to set every single row in the table to its correct individual height. With each row at its correct height, the JTable overall ends up being the correct height, which seems to resolve the print truncation issue. The following code shows visiting all the renderers to do just that:

for (int colIdx = 0; colIdx < jTable.getColumnCount(); colIdx++)
{
    for (int rowIdx = 0; rowIdx < jTable.getRowCount(); rowIdx++)
    {
        TableColumnModel columnModel = jTable.getColumnModel();
        TableColumn column = columnModel.getColumn(colIdx);
        TableCellRenderer renderer = column.getCellRenderer();
        Object cellValue = jTable.getValueAt(rowIdx, colIdx);
        renderer.getTableCellRendererComponent(jTable, cellValue, 
                    false, false, rowIdx, colIdx);
    }
}

This manual visitation of all cell renderers bypasses JTable's optimization of only invoking the cell renderers of on-screen cells. As such, you may want to only execute this workaround code after the user has requested printing (but before calling the print method on the JTable). This lets the JTable optimization stay in effect if the user is just using the GUI and not printing.

涙—继续流 2024-10-22 12:31:49

我必须稍微修改 Mike Clarks 的解决方案才能使其适合我:

for (int colIdx = 0; colIdx < jTable.getColumnCount(); colIdx++) {
    for (int rowIdx = 0; rowIdx < jTable.getRowCount(); rowIdx++) {
        TableCellRenderer renderer = jTable.getCellRenderer(rowIdx, colIdx);
        Object cellValue = jTable.getValueAt(rowIdx, colIdx);
        renderer.getTableCellRendererComponent(jTable, cellValue,
                false, false, rowIdx, colIdx);
    }
}

I had to slightly modify Mike Clarks solution to make it work for me:

for (int colIdx = 0; colIdx < jTable.getColumnCount(); colIdx++) {
    for (int rowIdx = 0; rowIdx < jTable.getRowCount(); rowIdx++) {
        TableCellRenderer renderer = jTable.getCellRenderer(rowIdx, colIdx);
        Object cellValue = jTable.getValueAt(rowIdx, colIdx);
        renderer.getTableCellRendererComponent(jTable, cellValue,
                false, false, rowIdx, colIdx);
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文