Perché l’intestazione JTable non appare nell’immagine?

Stavo offrendo consigli su come acquisire un’immagine di dati tabulari su Java API o Tool per convertire i dati tabulari in file immagine PNG – quando l’OP richiedeva un campione di codice. Risulta essere più difficile di quanto pensassi! L’intestazione JTable scompare dal PNG che il codice scrive.

PNG

PNG

Immagine dello schermo

inserisci la descrizione dell'immagine qui

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

Nota: ho controllato la class Screen Image di camickr e incluso una chiamata al metodo doLayout(Component) . Il metodo è utile se un Component non è mai stato realizzato sullo schermo, ma non ha alcun effetto su questo codice (che apre il pannello contenente la tabella in un riquadro delle opzioni prima di provare a renderlo).

Cosa è necessario per ottenere il rendering dell’intestazione della tabella?

Aggiornamento 1

Cambiando la linea ..

  p.paint(g); 

..to (con un’appropriata importazione) ..

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

..produces ..

2 ° prova

Continuerò a modificarlo.

Aggiornamento 2

kleopatra (strategia 1) e camickr (strategia 2) hanno fornito una risposta a ciascuno di essi, entrambi funzionano, e nessuno dei due richiede l’aggiunta di JTable a un componente fittizio (che è un enorme hack IMO).

Mentre la strategia 2 si ritaglia (o espande) in “solo il tavolo”, la 1a strategia catturerà il pannello contenente la tabella. Ciò diventa problematico se la tabella contiene molte voci, mostrando un’immagine di una tabella troncata con una barra di scorrimento.

Mentre la strategia 1 potrebbe essere ulteriormente ottimizzata per aggirare il problema, Mi piace molto l’ordinata semplicità della strategia 2, quindi ottiene il segno di spunta.

Come sottolineato da kleopatra, non c’era bisogno di “modificare”. Quindi ci riproverò ..

Aggiornamento 3

Questa è l’immagine prodotta dai metodi proposti da Camickr e Kleopatra. L’avrei messo due volte, ma ai miei occhi, sono identici (anche se non ho fatto un confronto pixel per pixel).

JTable in Nimbus PLAF

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

Entrambi raggiungono l’objective. Usando il metodo di Camickr puoi sfruttare l’ulteriore potenza dell’API ScreenImage. Usando il metodo di kleopatra – circa una dozzina di linee (meno i commenti e lo spazio bianco) del puro J2SE.

Mentre ScreenImage è una class che userò e raccomanderò in futuro, l’altro approccio che usa il core J2SE è quello che probabilmente utilizzerei per questa circostanza esatta.

Quindi, mentre il ‘ticchettio’ rimarrà con Camickr, la taglia andrà a Kleopatra.

Non era un requisito di questo thread rendere la tabella senza prima visualizzarla, ma quello era l’objective finale

ScreenImage gestisce questo.

È necessario aggiungere manualmente l’intestazione allo scrollpane.

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

Nota: il suggerimento di Kleopatra di usare addNotify () sul pannello non funzionerà con ScreenImage. Il metodo addNotify () rende il componente displayable e il codice ScreenImage disegna solo i componenti per i componenti non visualizzabili. Potrei cercare di renderlo più generale.

Quello era un tipo tosto

Era confuso dal fatto che quell’intestazione non comparisse affatto sull’immagine: non era ritagliata o qualcosa del genere, non era affatto dipinta. Il motivo è che … al momento di dipingere il pannello sull’immagine, l’intestazione non fa più parte della gerarchia. Quando si chiude l’opzionePane, table.removeNotify rimuove l’intestazione. Per aggiungerlo di nuovo, chiama addNotify, come in questo snippet:

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

[risolto in modifica] Quello che ancora non capisco perché il pannello si presenta vuoto senza prima essere stato mostrato nel optionPane – in genere una combinazione di ..

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

… andrà bene

modificare

ultima confusione risolta: per forzare una ri-layout ricorsiva del riquadro, tutti i contenitori lungo la linea devono essere spinti a credere che abbiano un peer – la convalida è ottimizzata per non fare nulla se non lo è. Fare il addNotify sul pannello (anziché sul tavolo) e validare fa il trucco

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

Non testato per gli effetti collaterali.

Modifica 2

Solo brontolando un po ‘sul ..

la strategia 1 potrebbe essere ulteriormente ottimizzata per aggirare quella [colonna troncata]

In realtà non è necessario alcun tweak (o lo stesso tweak necessario per ScreenImage, dipende da cosa consideri un tweak 🙂 – entrambi richiedono di fare in modo che JScrollPane non faccia il suo lavoro impostando prefViewportSize della tabella, la stessa identica linea in entrambi:

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

L’aggiunta manuale di JPanel a un JFrame di tua creazione, non solo a quello utilizzato da JOptionPane, sembra risolverlo.

L’introduzione di JFrame ti consente di saltare la finestra di dialogo iniziale, se lo desideri.

 // ...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")); 

Immagine della tabella

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

per favore, questa non è una risposta, solo un commento e un po ‘…. 🙂

@ Andrew Thompson, se ho capito correttamente per esempio , basta fare riferimento a J/Components è dentro / passato Graphics2D g2 = (Graphics2D) g; e non accettato TableHeader .... referenziato / derivato TableHeader .... probabilmente (sono pigro per controllarlo) qualcosa in API

spettacoli con codice sfacciato 🙂

 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 

inserisci la descrizione dell'immagine qui

 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() { } } 

Prova a disabilitare il doppio buffering sul componente (o globalmente) prima di stamparlo ( c.setDoubleBuffered(false) ). Apparentemente ha un effetto 🙂 (vedi http://www.java2s.com/Code/Java/2D-Graphics-GUI/PrintSwingcomponents.htm e http://www.apl.jhu.edu/~hall/java/ Swing-Tutorial / Swing-Tutorial-Printing.html )