Почему заголовок JTable не отображается на изображении? - PullRequest
28 голосов
/ 10 сентября 2011

Я предлагал совет по захвату изображения табличных данных на Java API или Инструменте для преобразования табличных данных в файл изображения PNG - когда ОП запросил образец кода.Оказывается сложнее, чем я думал!Заголовок JTable исчезает из PNG, который записан в коде.

PNG

PNG

Снимок экрана

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

Примечание. Я проверил класс изображения Camickr и включил вызов метода doLayout(Component).Этот метод полезен в том случае, если Component никогда не был реализован на экране, но не влияет на этот код (при котором панель, содержащая таблицу, появляется на панели параметров перед попыткой ее визуализации).

Чтонужен для того, чтобы получить заголовок таблицы для рендеринга?

Обновление 1

Изменение строки ..

        p.paint(g);

.. на (с соответствующим импортом)..

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

.. производит ..

2nd try

Я буду дорабатывать его.

Обновление 2

kleopatra (стратегия 1) и camickr (стратегия 2) предоставили ответы каждому, оба из которых работают, и ни один из которых не требует добавления JTable к фиктивному компоненту (который является огромным хаком IMO).

В то время как стратегия 2 обрезает (или расширяет) до «просто стола», 1-я стратегия захватит панель, содержащую таблицу.Это становится проблематичным, если таблица содержит много записей с изображением усеченной таблицы с полосой прокрутки.

В то время как стратегия 1 может быть доработана, Iочень нравится аккуратная простота стратегии 2, поэтому она получает галочку.

Как указывал Клеопатра, «твик» не нужен.Поэтому я попробую еще раз ..

Обновление 3

Это изображение, полученное методами, предложенными как camickr, так и kleopatra.Я бы поставил его дважды, но, на мой взгляд, они идентичны (хотя я не делал попиксельное сравнение).

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

Оба достигаютЦель.Используя метод camickr, вы используете дополнительные возможности API ScreenImage.Используя метод Клеопатры - около десятка строк (без комментариев и пробелов) чистого J2SE.

Хотя ScreenImage - это класс, который я буду использовать и рекомендовать в будущем, другой подход, использующий ядро ​​J2SE, - это то, что я, вероятно, использовал бы для этого точного обстоятельства.

Так что, хотя «галочка» будетостаться с camickr, щедрость собирается Клеопатра.

Ответы [ 6 ]

15 голосов
/ 10 сентября 2011

это было непросто

Было озадачено, что этот заголовок вообще не показывался на изображении: он не был вырезан или что-то, он вообще не был нарисован.Причина этого заключается в том ... что во время рисования панели на изображении заголовок больше не является частью иерархии.При закрытии optionPane table.removeNotify удаляет заголовок.Чтобы добавить его еще раз, вызовите addNotify, как в следующем фрагменте:

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

[решено в правке] Что я до сих пор не понимаю, почему панель отображается пустой, если сначала ее не отображали в optionPane - обычнонекоторая комбинация ..

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

... сделает

Редактировать

Последнее решенное недоразумение: вызвать рекурсивное изменение макетапанель, все контейнеры вдоль линии должны быть убеждены, что у них есть одноранговый узел - validate оптимизирован, чтобы ничего не делать, если нет.Выполнение команды addNotify на панели (а не на столе) плюс подтверждение делает трюк

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

Не проверено на наличие побочных эффектов.

Edit 2

Просто немного ворча о стратегии ..

1 можно еще больше подправить, чтобы обойти эту [усеченную колонку таблицы]

На самом деле настройки нетНужен (или тот же твик, что и для ScreenImage, зависит от того, что вы считаете твиком :) - оба требуют, чтобы JScrollPane не выполнял свою работу, устанавливая prefViewportSize таблицы, точно такую ​​же строку в обоих:

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

    // strategy 2
    scroll.setColumnHeaderView(table.getTableHeader());
    table.setPreferredScrollableViewportSize(table.getPreferredSize());
11 голосов
/ 11 сентября 2011

Этот поток не требовал рендеринга таблицы без предварительного ее отображения, но это была конечная цель

ScreenImage обрабатывает это.

Вы должны вручную добавить заголовок в область прокрутки.

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

Примечание: предложение Клеопатры использовать addNotify () на панели не будет работать с ScreenImage.Метод addNotify () делает компонент displayable, а код ScreenImage будет размещать компоненты только для неотображаемых компонентов.Я мог бы попытаться сделать это более общим.

6 голосов
/ 10 сентября 2011

Ручное добавление JPanel к JFrame вашего собственного создания, а не только к тому, которое использует JOptionPane, кажется, решает эту проблему.

Введение JFrame позволяет пропустить начальный диалогдисплей, если хотите.

// ...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"));
5 голосов
/ 10 сентября 2011

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"));
    }
}
4 голосов
/ 10 сентября 2011

пожалуйста, это не ответ, просто комментарий и немного ....: -)

@ Эндрю Томпсон, если я правильно понял для пример , просто ссылка на J/Components находится внутри / в прошлом Graphics2D g2 = (Graphics2D) g; и не принимается ссылками / производными TableHeader .... вероятно (мне лень это проверять) что-то в API

фрагмент кода показывает: -)

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() {
    }
}
3 голосов
/ 10 сентября 2011

Попробуйте отключить двойную буферизацию на компоненте (или глобально) перед его печатью (c.setDoubleBuffered(false)). По-видимому, это имеет эффект :) (см. http://www.java2s.com/Code/Java/2D-Graphics-GUI/PrintSwingcomponents.htm и http://www.apl.jhu.edu/~hall/java/Swing-Tutorial/Swing-Tutorial-Printing.html)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...