2011-02-04 11 views
5

Ho una tabella J che utilizza JTextArea come TableCellRenderer, in modo che le celle della tabella possano utilizzare il ritorno a capo automatico. Il JTable viene visualizzato correttamente. Quando stampo la tabella su una stampante tramite JTable print method, l'output viene sempre troncato a circa il 60% dei dati. Ho provato diversi computer e diverse stampanti e diversi driver di stampa, diverse versioni di JVM (1.5, 1.6) ma nessuna di queste ha aiutato. Di seguito è riportata una classe principale Java autonoma che riproduce il problema. Qualche idea?Uscita di stampa JTable troncata

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; 
     } 
    } 
} 

risposta

0

Credo di aver trovato la causa principale e una soluzione. Il problema di stampa deriva dal fatto che il tavolo ha un'altezza sbagliata. La tabella ha un'altezza errata perché alcune delle righe della tabella hanno un'altezza errata. Alcune delle righe hanno un'altezza errata perché i loro renderer di celle non sono stati invocati da JTable (cioè, il loro metodo getTableCellRendererComponent non è stato chiamato). Il motivo per cui JTable sta saltando alcuni renderer di celle è l'ottimizzazione - i renderizzatori di celle salta sono per le celle che sono fuori dallo schermo - fuori dallo schermo significa che il rendering non è necessario; L'ottimizzazione di JTable in questo settore ha senso.

Tuttavia, i renderer sono l'unico posto nel programma in cui è impostata l'altezza corretta di ogni singola riga. La mia scelta di impostare l'altezza della riga nel renderer di celle è in qualche modo comprensibile perché il renderer di celle è nella posizione migliore per sapere quale altezza deve essere la riga.

Sembra il modo più straight-forward per risolvere questo programma di esempio è quello di chiamare manualmente getTableCellRendererComponent su ogni renderer di celle (questo dovrebbe essere fatto dopo aver modificato i dati del modello della tabella). Questo dà ai renderer l'opportunità di impostare ogni singola riga nella tabella alla sua altezza individuale corretta. Con ogni riga all'altezza corretta, il valore complessivo di JTable risulta essere l'altezza corretta, che sembra risolvere il problema del troncamento della stampa. Il codice seguente mostra visitare tutti i renderer per fare proprio questo:

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); 
    } 
} 

Questa visita manuale di tutti i renderer di celle bypassa l'ottimizzazione di JTable solo invocando i renderer di celle di cellule sullo schermo. Pertanto, è possibile che si desideri eseguire questo codice provvisorio solo dopo che l'utente ha richiesto la stampa (ma prima di chiamare il metodo di stampa su JTable). Ciò consente all'ottimizzazione di JTable di rimanere attiva se l'utente sta semplicemente utilizzando la GUI e non sta stampando.

+1

modo sbagliato: il renderer _must not_ cambiamento di stato del chiamante in alcun modo, mai. Sposta invece l'aggiornamento rowHeight nel ciclo che stai visualizzando in questo post. – kleopatra

+0

@kleopatra - grazie per il feedback. Vorrei aggiornare la mia risposta in risposta al tuo commento. Tuttavia, sono andato a cercare la documentazione della regola "il renderer non deve modificare lo stato del chiamante" e non sono riuscito a trovare nulla. Non sto dicendo che ti sbagli, voglio solo saperne di più e magari ottenere la verifica. Qualche idea su dove è documentato questo contratto? –

0

ho dovuto modificare un po 'la soluzione Mike Clarks per farlo funzionare per 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); 
    } 
}