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

PNG

Screen shot

Tutaj wpisz opis obrazka

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..

2. Próba

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).

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

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.
Author: Community, 2011-09-10

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.

 11
Author: camickr,
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());
 15
Author: kleopatra,
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"));
 6
Author: John Flatness,
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

Obraz tabeli

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"));
    }
}
 5
Author: Andrew Thompson,
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

Tutaj wpisz opis obrazka

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() {
    }
}
 4
Author: mKorbel,
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 )

 3
Author: Chris Dennett,
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