Pourquoi L'en-tête JTable n'apparaît-il pas dans l'image?

je proposais des conseils sur la capture d'une image de données tabulaires sur API Java ou outil pour convertir des données tabulaires dans le fichier image PNG - lorsque L'OP a demandé un échantillon de code. Il s'avère que c'est plus dur que je pensais! L'en-tête JTable disparaît du PNG que le code écrit.

PNG

PNG

capture d'Écran

enter image description here

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

Note: j'ai vérifié la classe Screen Image de camickr et j'ai inclus un appel à la méthode doLayout(Component) . La méthode est utile pour si un Component n'a jamais été réalisé à l'écran, mais n'a aucun effet sur ce code (qui Pop le panneau contenant la table dans un volet d'option avant d'essayer de la rendre).

Qu'est-ce qui est nécessaire pour obtenir l'en-tête de table à rendre?

"1519120920 mise à jour" Update 1

changement de ligne..

        p.paint(g);

..(avec une importation)..

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

..produire..

2nd try

je vais continuer à le retoucher.

Update 2

kleopatra (stratégie 1) & camickr (stratégie 2) ont fourni une réponse chacun, à la fois les travaux , et ni l'un ni l'autre nécessite d'ajouter le JTable à un composant dummy (qui est un énorme hack IMO).

alors que la stratégie 2 passera (ou élargira) à "juste la table", la première stratégie capturera le panneau contenant la table. Cela devient problématique si la table contient de nombreuses entrées, montrant une image d'une table tronquée avec une barre de défilement.

bien que la stratégie 1 pourrait être encore plus modifié pour se déplacer que, j'aime vraiment la simplicité soignée de la stratégie 2, donc il obtient le coup.

comme l'a souligné kleopatra, il n'y avait pas besoin de "tweak". Je vais donc essayer de nouveau..

Update 3

c'est l'image produite par les méthodes proposées par camickr et kleopatra. J'aurais mis deux fois, mais à mes yeux, ils sont identiques (bien que je n'ai pas fait un pixel par pixel comparaison).

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.
    /q/why-does-the-jtable-header-not-appear-in-the-image-24714/"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)));
    }
}

tous deux atteignent l'objectif. En utilisant la méthode de camickr, vous utilisez la puissance supplémentaire de L'API ScreenImage. En utilisant la méthode de kleopatra-environ une douzaine de lignes (moins les commentaires et l'espace blanc) de J2SE pure.

bien que ScreenImage soit une classe que j'utiliserai et que je recommanderai à l'avenir, l'autre approche utilisant le core J2SE est probablement celle que j'utiliserais pour cette circonstance exacte.

alors tant que le " tic " restera avec camickr, la prime va à kleopatra.

28
demandé sur Community 2011-09-10 09:17:28

6 réponses

Ce n'était pas une exigence de ce thread pour rendre la table sans afficher, mais c'est l'objectif ultime

ScreenImage gère ceci.

vous devez ajouter manuellement l'en-tête au 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"));
    }
}

Note: La Suggestion de Kleopatra d'utiliser addNotify() sur le panneau ne fonctionnera pas avec ScreenImage. La méthode addNotify() rend le composant displayable et le code de ScreenImage n'affichera que les composants non affichables. Je pourrais chercher à faire de ce plus général.

11
répondu camickr 2011-09-11 02:41:24

c'était difficile

était perplexe que cette tête n'apparaisse pas du tout sur l'image: elle n'était pas coupée ou quelque chose, elle n'était pas peinte du tout. La raison en est que .. qu'au moment de peindre le panneau de l'image, l'en-tête n'est plus une partie de la hiérarchie. Lors de la fermeture de l'espace optionPane, table.removeNotify supprime l'en-tête. Pour l'ajouter à nouveau, appelez addNotify, comme dans cet extrait:

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

[résolu en édition] Ce que je ne comprends toujours pas pourquoi le panneau apparaît vide sans avoir d'abord été montré dans l'optique - typiquement une combinaison de ..

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

... allons le faire

Modifier

dernière confusion résolue: pour forcer une re-disposition récursive du Panneau, tous les conteneurs le long de la ligne doivent être amenés à croire qu'ils ont un peer - validate est optimisé pour ne rien faire sinon. Faire l'addNotify sur le panneau (au lieu de sur la table) plus valider fait le tour

    //        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 testé pour les effets secondaires.

Edit 2

grommelant un peu sur le..

stratégie 1 peut encore être modifié pour contourner [tronquée colonne de table]

en Fait, il n'y a pas de tweak nécessaire (ou le même tweak nécessaires pour ScreenImage, dépend de ce que vous considérez un tweak:) - les deux nécessitent de faire le JScrollPane pas faire son travail en mettant le prefViewportSize de la table, la même ligne exacte dans les deux:

    // strategy 1
    table.setPreferredScrollableViewportSize(table.getPreferredSize());
    p.addNotify();

    // strategy 2
    scroll.setColumnHeaderView(table.getTableHeader());
    table.setPreferredScrollableViewportSize(table.getPreferredSize());
15
répondu kleopatra 2011-09-13 04:52:00

ajouter manuellement votre JPanel à un JFrame de votre propre création, pas seulement celle que JOptionPane utilise, semble résoudre ce problème.

introduisant le JFrame vous permet de sauter l'affichage de dialogue initial, si vous voulez.

// ...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
répondu John Flatness 2011-09-10 06:24:06

Table image

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
répondu Andrew Thompson 2011-09-10 05:42:11

s'il vous plaît, ce n'est pas la réponse à tout commentaire et peu .... :- )

@Andrew Thompson, si j'ai bien compris pour exemple , juste référence à la J/Components est à l'intérieur / passé Graphics2D g2 = (Graphics2D) g; et non accepté référencé/dérivé TableHeader .... probablement (je paresse de vérifier cela) quelque chose dans L'API

code ciselée montre :-)

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

enter image description here

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
répondu mKorbel 2011-09-10 08:08:06

essayez de désactiver le double tampon sur le composant (ou globalement) avant de l'imprimer ( c.setDoubleBuffered(false) ). Apparemment, il a un effet :) (voir http://www.java2s.com/Code/Java/2D-Graphics-GUI/PrintSwingcomponents.htm et http://www.apl.jhu.edu/~hall/java/Swing-Tutorial/Swing-Tutorial-Printing.html )

3
répondu Chris Dennett 2011-09-10 05:41:44