Dlaczego nagłówek JTable nie pojawia się na obrazku?
Oferowałem porady dotyczące przechwytywania obrazu danych tabelarycznych na Java API lub narzędzia do konwersji danych tabelarycznych do pliku PNG - gdy OP zażądał próbki kodu. Okazuje się być trudniejsze niż myślałem! Nagłówek JTable
znika z PNG, który zapisuje kod.
PNG
Screen shot
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"));
}
}
Uwaga: sprawdziłem w klasie Camickr Screen Image i włączyłem wywołanie metody doLayout(Component)
. Na metoda jest przydatna, jeśli Component
nigdy nie została zrealizowana na ekranie, ale nie ma wpływu na ten kod (który wyskakuje z panelu zawierającego tabelę w okienku opcji przed próbą jej wyrenderowania).
Co jest potrzebne do renderowania nagłówka tabeli?
Update 1
Zmiana linii..
p.paint(g);
..do (z odpowiednim importem)..
p.paint(g);
JTableHeader h = table.getTableHeader();
h.paint(g);
..produkuje..
Będę go poprawiał.Update 2
Kleopatra (strategy 1) & camickr (strategy 2) dostarczyły odpowiedzi, które działają, i żadna z nich nie wymaga dodania JTable
do atrapy komponentu (który jest ogromnym hakerem IMO).
Podczas gdy strategia 2 zostanie przycięta (lub rozwinięta) do "tylko tabela", strategia 1.przechwyci panel zawierający tabelę. Staje się to problematyczne, jeśli tabela zawiera wiele pozycji, pokazując obraz ściętej tabeli z paskiem przewijania.
podczas gdy strategia 1 może być dodatkowo poprawione, aby to obejść, naprawdę podoba mi się zgrabna prostota strategii 2, więc dostaje kleszcza.
Jak zauważył kleopatra, nie było potrzeby "ulepszania". Spróbuję jeszcze raz..
Update 3
Jest to obraz wytworzony metodami zaproponowanymi zarówno przez camickra, jak i Kleopatrę. Ująłbym to dwa razy, ale moim zdaniem są identyczne (choć nie zrobiłem porównania piksel po pikselu).
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)));
}
}
Obie osiągają cel. Korzystając z metody camickr, wykorzystujesz dalszą moc interfejsu API ScreenImage. Wykorzystanie metody Kleopatry - około kilkunastu linijek (bez komentarzy i białych spacji) czystego J2SE.
Podczas gdy ScreenImage jest klasą, której będę używać i polecać w przyszłości, inne podejście wykorzystujące rdzeń J2SE jest tym, czego prawdopodobnie użyłbym w tych okolicznościach.
Więc podczas gdy "kleszcz" zostanie z camickrem, bounty trafi do Kleopatry.6 answers
Nie wymagano od tego wątku renderowania tabeli bez uprzedniego wyświetlenia jej, ale to był ostateczny cel
ScreenImage radzi sobie z tym.
Musisz ręcznie dodać nagłówek do przycisku przewijania.
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"));
}
}
Uwaga: sugerowane przez Kleopatrę użycie addNotify () na panelu nie będzie działać z ScreenImage. Metoda addNotify () tworzy komponent displayable
, A Kod ScreenImage będzie określał tylko komponenty Dla komponentów, które nie są wyświetlane. I to może być bardziej ogólne.
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-11 02:41:24
To było trudne.]}
Zdziwiło mnie, że nagłówek w ogóle nie pojawił się na obrazie: nie był przycięty czy coś, nie był w ogóle malowany. Powodem tego jest .. że w momencie malowania panelu na obraz, nagłówek nie jest już częścią hierarchii. Podczas zamykania opcjipane, tabela.removeNotify usuwa nagłówek. Aby dodać go ponownie, wywołaj addNotify, jak w tym fragmencie:
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();
[rozwiązane w edycji] co dalej nie rozumiem dlaczego panel pokazuje up empty without first have been showed in the optionPane-typowo jakaś kombinacja ..
p.doLayout();
p.setSize(p.getPreferredSize())
... zrobi
Edit
Ostatnie zamieszanie rozwiązane: aby wymusić rekurencyjny układ panelu, cały kontener wzdłuż linii musi być doprowadzony do przekonania, że ma weryfikację peer - validate jest zoptymalizowany tak, aby nic nie robił, jeśli nie. W tym celu należy wykonać polecenie addNotify na panelu (zamiast na stole) oraz 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)));
Nie testowane dla skutki uboczne.
Edit 2
Tylko trochę narzekam..Strategia 1 może być dodatkowo ulepszona, aby ominąć tę [ściętą kolumnę tabeli]
Właściwie nie ma potrzeby dostosowywania (lub to samo, co w przypadku ScreenImage, zależy od tego, co uznasz za poprawkę:) - oba wymagają, aby JScrollPane nie wykonało swoje zadanie, ustawiając prefViewportSize tabeli, dokładnie ten sam wiersz w obu:
// strategy 1
table.setPreferredScrollableViewportSize(table.getPreferredSize());
p.addNotify();
// strategy 2
scroll.setColumnHeaderView(table.getTableHeader());
table.setPreferredScrollableViewportSize(table.getPreferredSize());
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-13 04:52:00
Ręczne dodawanie JPanel
do JFrame
własnego stworzenia, nie tylko tego, którego używa JOptionPane, wydaje się rozwiązać ten problem.
Wprowadzenie JFrame
pozwala pominąć początkowe okno dialogowe, jeśli chcesz.
// ...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"));
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-10 06:24:06
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"));
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-10 05:42:11
Proszę, to nie jest odpowiedź tylko komentarz i trochę .... :-)
@ Andrew Thompson, jeśli dobrze zrozumiałem dla przykład , tylko odwołanie do J/Components
jest wewnątrz / przeszłości Graphics2D g2 = (Graphics2D) g;
i nie jest akceptowane odwołuje/pochodną TableHeader ....
prawdopodobnie (leniwie to sprawdzić) coś w API
Code snipped shows: -)
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
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() {
}
}
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-10 08:08:06
Spróbuj wyłączyć podwójne buforowanie komponentu (lub globalnie) przed jego wydrukowaniem (c.setDoubleBuffered(false)
). Widocznie ma efekt :) (zobacz http://www.java2s.com/Code/Java/2D-Graphics-GUI/PrintSwingcomponents.htm i http://www.apl.jhu.edu / ~hall/java/Swing-Tutorial/Swing-Tutorial-Printing.html )
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2011-09-10 05:41:44