缩放JComponent以适合页面页面

时间:2016-03-02 22:32:53

标签: java swing intellij-idea printing

Althougt我努力尽力而为,无法应对我的问题。问题是这样的:我想从我的框架中打印JComponent,确切地说是JPanel,但它太大了,无法以纵向或横向方式放入标准A4页面。为了完成这项工作,我在这里实现了代码Fit/Scale JComponent to page being printed,因此我的ComponentPrintable类看起来像这样:

public class ComponentPrintable extends JPanel implements Printable {

private Component comp;

public ComponentPrintable(Component comp)
{
    this.comp = comp;
}

@Override
public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException
{
    if (pageIndex > 0) {
        return Printable.NO_SUCH_PAGE;
    }

    Dimension componentSize = comp.getPreferredSize();
    comp.setSize(componentSize);

    Dimension printSize = new Dimension();
    printSize.setSize(pageFormat.getImageableWidth(), pageFormat.getImageableHeight());

    double scaleFactor = getScaleFactorToFit(componentSize, printSize);

    if (scaleFactor > 1d) {
        scaleFactor = 1d;
    }

    double scaleWidth = componentSize.width * scaleFactor;
    double scaleHeight = componentSize.height * scaleFactor;

    Graphics2D g2 = (Graphics2D)graphics;

    double x = ((pageFormat.getImageableWidth() - scaleWidth)/2d) + pageFormat.getImageableX();
    double y = ((pageFormat.getImageableHeight() - scaleHeight)/2d) + pageFormat.getImageableY();

    AffineTransform at = new AffineTransform();

    at.translate(x, y);
    at.scale(scaleFactor, scaleFactor);
    g2.transform(at);
    comp.printAll(g2);
    g2.dispose();
    comp.revalidate();

    return PAGE_EXISTS;
}

private double getScaleFactorToFit(Dimension original, Dimension toFit) {

    double dScale = 1d;

    if (original != null && toFit != null) {

        double dScaleWidth = getScaleFactor(original.width, toFit.width);
        double dScaleHeight = getScaleFactor(original.height, toFit.height);

        dScale = Math.min(dScaleHeight, dScaleWidth);
    }

    return dScale;
}

private double getScaleFactor(int iMasterSize, int iTargetSize) {

    double dScale = 1;
    if (iMasterSize > iTargetSize) {

        dScale = (double) iTargetSize / (double) iMasterSize;
    }

    else {
        dScale = (double) iTargetSize / (double) iMasterSize;
    }

    return dScale;
}
}

现在我在IntelliJ GUI设计器中创建了两个GUI表单MainWindowTmpPanel也是在设计器中创建的,所以它是正式的JFrame )。

这两个看起来像这样(两个XML文件在下面的链接中提供) https://www.dropbox.com/sh/lmg8xcj2cghgqzn/AADHgX6Esm30iS7r6GeVA4_0a?dl=0

Binded classes:

public class TmpPanel extends JFrame
{

private JPanel panel1;
private JPanel checkBoxPanel;
private JCheckBox fee;
private JCheckBox administration;
private JCheckBox water;
private JCheckBox booking;
private JCheckBox electricity;
private JCheckBox toilet;
private JPanel printPanel;
private JPanel dataPanel;
private JPanel generalTablePanel;
private JPanel summaryPanel;
private JLabel fakturaNr;
private JLabel CopyOriginal;
private JPanel SalePanel;
private JPanel SposobZaplatyPanel;
private JPanel NabywcaPanel;
private JPanel SprzedawcaPanel;
private JPanel servicesTablePanel;
private JPanel summaryTablePanel;

private JPanel[] panels = {panel1, checkBoxPanel, printPanel, dataPanel, generalTablePanel, summaryTablePanel, summaryPanel,
        SalePanel, SposobZaplatyPanel, NabywcaPanel, SprzedawcaPanel, servicesTablePanel};

public TmpPanel()
{
    for(JPanel x: panels)
    {
        x.repaint();
        x.validate();
        System.out.println(x.isValid());
    }
    setContentPane(panel1);

    System.out.println(getContentPane().isValid());
    System.out.println(printPanel.isValid());
}

public Component getPrintablePanel()
{

    return printPanel;
}
}

public class MainWindow extends JFrame implements ActionListener
{
private JTabbedPane tabbedPane1;
private JPanel rootPanel;
private JButton printButton;
private JButton newClientButton;
private JButton removeClientButton;

public MainWindow()
{
    super("Fakturowanie");
    setContentPane(rootPanel);
    setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    setResizable(false);

    tabbedPane1.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT);

    newClientButton.addActionListener(this);
    removeClientButton.addActionListener(this);
    printButton.addActionListener(this);
    pack();
    setVisible(true);
}

@Override
public void actionPerformed(ActionEvent e)
{
    Object button = e.getSource();

    TmpPanel tmp = new TmpPanel();

    if(button == newClientButton)
    {
        String name = JOptionPane.showInputDialog(this, "Nazwa sprzedawcy:\n", "Nowy sprzedawca",
                JOptionPane.PLAIN_MESSAGE);

        if((name != null) && (name.length() > 0))
        {
            tabbedPane1.addTab(name, tmp.getContentPane());
            pack();
        }
    }

    if(button == removeClientButton)
    {
        tabbedPane1.remove(tabbedPane1.getSelectedComponent());
    }

    if(button == printButton)
    {
        System.out.println(tabbedPane1.isValid());
        printComponent(tmp.getPrintablePanel());
    }
}

public void printComponent(Component comp)
{
    PrinterJob printerJob = PrinterJob.getPrinterJob();
    PageFormat pf = printerJob.pageDialog(printerJob.defaultPage());
    printerJob.setPrintable(new ComponentPrintable(comp), pf);

    if(printerJob.printDialog())
    {
        try
        {
            printerJob.print();
        }

        catch (PrinterException e1)
        {
            JOptionPane.showMessageDialog(this, "Błąd drukowania", "Błąd", JOptionPane.OK_OPTION);
        }
    }
}
}

现在我的问题如下:我想从printPanel打印TmpPanel以便它适合页面。以前在printButton听众中我有

printComponent(tabbedPane1.getSelectedComponent());

它完美无缺。但是当我输入

printComponent(tmp.getPrintablePanel());

我只获得printPanel的背景色(我将其设为黑色以使其在空白页面上可见)。我尝试了extend JPanel, extend JFrame, add, setContentPane, getContentPane等所有组合,但没有任何效果。我只是设法找到了这个小东西:当我输入

System.out.println(tabbedPane1.getSelectedComponent().isValid());

它返回true。当我尝试在TmpPanel中验证或重新绘制每个组件时,isValid方法会为每个组件返回false

我想这就是为什么在按下“打印”按钮后我只能看到printPanel背景的黑色矩形。

为什么即使两个表单都是在设计器中创建的,以及如何使其工作?

2 个答案:

答案 0 :(得分:1)

因此,正如我所看到的,问题的一部分是一些组件在它们被附加到本地对等体(或实现的窗口)之前不想呈现。

有两种选择,您只需打印已在屏幕上的实时组件即可。这是一个问题,因为ComponentPrintable会改变组件的大小,这将影响实时组件,使用户感到有点不愉快。

第二个选项是创建组件的新实例并将其放在另一个JFrame上。棘手的部分是让框架自我实现并创建一个本地同伴。

幸运的是,我们知道有几种方法可以做到这一点,pack就是其中之一。

以下示例取自Using Text Components示例,因为它是一个实际复杂的组件。

当您点击打印时,它会生成此组件的新实例,将其添加到JFrame,打包框架然后打印该组件。

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.print.PageFormat;
import java.awt.print.Printable;
import static java.awt.print.Printable.PAGE_EXISTS;
import java.awt.print.PrinterException;
import java.awt.print.PrinterJob;
import java.io.IOException;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JEditorPane;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import javax.swing.JTextPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.text.BadLocationException;
import javax.swing.text.Style;
import javax.swing.text.StyleConstants;
import javax.swing.text.StyleContext;
import javax.swing.text.StyledDocument;

public class Main {

    public static void main(String[] args) {
        new Main();
    }

    public Main() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
                    ex.printStackTrace();
                }


                JButton print = new JButton("Print");
                print.addActionListener(new ActionListener() {
                    @Override
                    public void actionPerformed(ActionEvent e) {
                        TextSamplerDemo demo = new TextSamplerDemo();
                        JFrame test = new JFrame();
                        test.add(demo);
                        test.pack();
                        ComponentPrintable printable = new ComponentPrintable(demo);
                        PrinterJob printerJob = PrinterJob.getPrinterJob();
                        PageFormat pf = printerJob.pageDialog(printerJob.defaultPage());
                        printerJob.setPrintable(printable, pf);

                        if (printerJob.printDialog()) {
                            try {
                                printerJob.print();
                            } catch (PrinterException e1) {
                                JOptionPane.showMessageDialog(null, "Printing failed", "Print", JOptionPane.OK_OPTION);
                            }
                        }
                    }
                });

                JFrame frame = new JFrame("Testing");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                TextSamplerDemo demo = new TextSamplerDemo();
                frame.add(demo);
                frame.add(print, BorderLayout.SOUTH);
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    public class ComponentPrintable extends JPanel implements Printable {

        private Component comp;

        public ComponentPrintable(Component comp) {
            this.comp = comp;
        }

        @Override
        public int print(Graphics graphics, PageFormat pageFormat, int pageIndex) throws PrinterException {
            if (pageIndex > 0) {
                return Printable.NO_SUCH_PAGE;
            }

            Dimension componentSize = comp.getPreferredSize();
            comp.setSize(componentSize);

            Dimension printSize = new Dimension();
            printSize.setSize(pageFormat.getImageableWidth(), pageFormat.getImageableHeight());

            double scaleFactor = getScaleFactorToFit(componentSize, printSize);

            if (scaleFactor > 1d) {
                scaleFactor = 1d;
            }

            double scaleWidth = componentSize.width * scaleFactor;
            double scaleHeight = componentSize.height * scaleFactor;

            Graphics2D g2 = (Graphics2D) graphics;

            double x = ((pageFormat.getImageableWidth() - scaleWidth) / 2d) + pageFormat.getImageableX();
            double y = ((pageFormat.getImageableHeight() - scaleHeight) / 2d) + pageFormat.getImageableY();

            AffineTransform at = new AffineTransform();

            at.translate(x, y);
            at.scale(scaleFactor, scaleFactor);
            g2.transform(at);
            comp.printAll(g2);
            g2.dispose();
            comp.revalidate();

            return PAGE_EXISTS;
        }

        private double getScaleFactorToFit(Dimension original, Dimension toFit) {

            double dScale = 1d;

            if (original != null && toFit != null) {

                double dScaleWidth = getScaleFactor(original.width, toFit.width);
                double dScaleHeight = getScaleFactor(original.height, toFit.height);

                dScale = Math.min(dScaleHeight, dScaleWidth);
            }

            return dScale;
        }

        private double getScaleFactor(int iMasterSize, int iTargetSize) {

            double dScale = 1;
            if (iMasterSize > iTargetSize) {

                dScale = (double) iTargetSize / (double) iMasterSize;
            } else {
                dScale = (double) iTargetSize / (double) iMasterSize;
            }

            return dScale;
        }
    }

    public class TextSamplerDemo extends JPanel
            implements ActionListener {

        private String newline = "\n";
        protected static final String textFieldString = "JTextField";
        protected static final String passwordFieldString = "JPasswordField";
        protected static final String ftfString = "JFormattedTextField";
        protected static final String buttonString = "JButton";

        protected JLabel actionLabel;

        public TextSamplerDemo() {
            setLayout(new BorderLayout());

            //Create a regular text field.
            JTextField textField = new JTextField(10);
            textField.setActionCommand(textFieldString);
            textField.addActionListener(this);

            //Create a password field.
            JPasswordField passwordField = new JPasswordField(10);
            passwordField.setActionCommand(passwordFieldString);
            passwordField.addActionListener(this);

            //Create a formatted text field.
            JFormattedTextField ftf = new JFormattedTextField(
                    java.util.Calendar.getInstance().getTime());
            ftf.setActionCommand(textFieldString);
            ftf.addActionListener(this);

            //Create some labels for the fields.
            JLabel textFieldLabel = new JLabel(textFieldString + ": ");
            textFieldLabel.setLabelFor(textField);
            JLabel passwordFieldLabel = new JLabel(passwordFieldString + ": ");
            passwordFieldLabel.setLabelFor(passwordField);
            JLabel ftfLabel = new JLabel(ftfString + ": ");
            ftfLabel.setLabelFor(ftf);

            //Create a label to put messages during an action event.
            actionLabel = new JLabel("Type text in a field and press Enter.");
            actionLabel.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0));

            //Lay out the text controls and the labels.
            JPanel textControlsPane = new JPanel();
            GridBagLayout gridbag = new GridBagLayout();
            GridBagConstraints c = new GridBagConstraints();

            textControlsPane.setLayout(gridbag);

            JLabel[] labels = {textFieldLabel, passwordFieldLabel, ftfLabel};
            JTextField[] textFields = {textField, passwordField, ftf};
            addLabelTextRows(labels, textFields, gridbag, textControlsPane);

            c.gridwidth = GridBagConstraints.REMAINDER; //last
            c.anchor = GridBagConstraints.WEST;
            c.weightx = 1.0;
            textControlsPane.add(actionLabel, c);
            textControlsPane.setBorder(
                    BorderFactory.createCompoundBorder(
                            BorderFactory.createTitledBorder("Text Fields"),
                            BorderFactory.createEmptyBorder(5, 5, 5, 5)));

            //Create a text area.
            JTextArea textArea = new JTextArea(
                    "This is an editable JTextArea. "
                    + "A text area is a \"plain\" text component, "
                    + "which means that although it can display text "
                    + "in any font, all of the text is in the same font."
            );
            textArea.setFont(new Font("Serif", Font.ITALIC, 16));
            textArea.setLineWrap(true);
            textArea.setWrapStyleWord(true);
            JScrollPane areaScrollPane = new JScrollPane(textArea);
            areaScrollPane.setVerticalScrollBarPolicy(
                    JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
            areaScrollPane.setPreferredSize(new Dimension(250, 250));
            areaScrollPane.setBorder(
                    BorderFactory.createCompoundBorder(
                            BorderFactory.createCompoundBorder(
                                    BorderFactory.createTitledBorder("Plain Text"),
                                    BorderFactory.createEmptyBorder(5, 5, 5, 5)),
                            areaScrollPane.getBorder()));

            //Create an editor pane.
            JEditorPane editorPane = createEditorPane();
            JScrollPane editorScrollPane = new JScrollPane(editorPane);
            editorScrollPane.setVerticalScrollBarPolicy(
                    JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
            editorScrollPane.setPreferredSize(new Dimension(250, 145));
            editorScrollPane.setMinimumSize(new Dimension(10, 10));

            //Create a text pane.
            JTextPane textPane = createTextPane();
            JScrollPane paneScrollPane = new JScrollPane(textPane);
            paneScrollPane.setVerticalScrollBarPolicy(
                    JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
            paneScrollPane.setPreferredSize(new Dimension(250, 155));
            paneScrollPane.setMinimumSize(new Dimension(10, 10));

            //Put the editor pane and the text pane in a split pane.
            JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT,
                    editorScrollPane,
                    paneScrollPane);
            splitPane.setOneTouchExpandable(true);
            splitPane.setResizeWeight(0.5);
            JPanel rightPane = new JPanel(new GridLayout(1, 0));
            rightPane.add(splitPane);
            rightPane.setBorder(BorderFactory.createCompoundBorder(
                    BorderFactory.createTitledBorder("Styled Text"),
                    BorderFactory.createEmptyBorder(5, 5, 5, 5)));

            //Put everything together.
            JPanel leftPane = new JPanel(new BorderLayout());
            leftPane.add(textControlsPane,
                    BorderLayout.PAGE_START);
            leftPane.add(areaScrollPane,
                    BorderLayout.CENTER);

            add(leftPane, BorderLayout.LINE_START);
            add(rightPane, BorderLayout.LINE_END);
        }

        private void addLabelTextRows(JLabel[] labels,
                JTextField[] textFields,
                GridBagLayout gridbag,
                Container container) {
            GridBagConstraints c = new GridBagConstraints();
            c.anchor = GridBagConstraints.EAST;
            int numLabels = labels.length;

            for (int i = 0; i < numLabels; i++) {
                c.gridwidth = GridBagConstraints.RELATIVE; //next-to-last
                c.fill = GridBagConstraints.NONE;      //reset to default
                c.weightx = 0.0;                       //reset to default
                container.add(labels[i], c);

                c.gridwidth = GridBagConstraints.REMAINDER;     //end row
                c.fill = GridBagConstraints.HORIZONTAL;
                c.weightx = 1.0;
                container.add(textFields[i], c);
            }
        }

        public void actionPerformed(ActionEvent e) {
            String prefix = "You typed \"";
            if (textFieldString.equals(e.getActionCommand())) {
                JTextField source = (JTextField) e.getSource();
                actionLabel.setText(prefix + source.getText() + "\"");
            } else if (passwordFieldString.equals(e.getActionCommand())) {
                JPasswordField source = (JPasswordField) e.getSource();
                actionLabel.setText(prefix + new String(source.getPassword())
                        + "\"");
            } else if (buttonString.equals(e.getActionCommand())) {
                Toolkit.getDefaultToolkit().beep();
            }
        }

        private JEditorPane createEditorPane() {
            JEditorPane editorPane = new JEditorPane();
            editorPane.setEditable(false);
            java.net.URL helpURL = TextSamplerDemo.class.getResource(
                    "TextSamplerDemoHelp.html");
            if (helpURL != null) {
                try {
                    editorPane.setPage(helpURL);
                } catch (IOException e) {
                    System.err.println("Attempted to read a bad URL: " + helpURL);
                }
            } else {
                System.err.println("Couldn't find file: TextSampleDemoHelp.html");
            }

            return editorPane;
        }

        private JTextPane createTextPane() {
            String[] initString
                    = {"This is an editable JTextPane, ", //regular
                        "another ", //italic
                        "styled ", //bold
                        "text ", //small
                        "component, ", //large
                        "which supports embedded components..." + newline,//regular
                        " " + newline, //button
                        "...and embedded icons..." + newline, //regular
                        " ", //icon
                        newline + "JTextPane is a subclass of JEditorPane that "
                        + "uses a StyledEditorKit and StyledDocument, and provides "
                        + "cover methods for interacting with those objects."
                    };

            String[] initStyles
                    = {"regular", "italic", "bold", "small", "large",
                        "regular", "button", "regular", "icon",
                        "regular"
                    };

            JTextPane textPane = new JTextPane();
            StyledDocument doc = textPane.getStyledDocument();
            addStylesToDocument(doc);

            try {
                for (int i = 0; i < initString.length; i++) {
                    doc.insertString(doc.getLength(), initString[i],
                            doc.getStyle(initStyles[i]));
                }
            } catch (BadLocationException ble) {
                System.err.println("Couldn't insert initial text into text pane.");
            }

            return textPane;
        }

        protected void addStylesToDocument(StyledDocument doc) {
            //Initialize some styles.
            Style def = StyleContext.getDefaultStyleContext().
                    getStyle(StyleContext.DEFAULT_STYLE);

            Style regular = doc.addStyle("regular", def);
            StyleConstants.setFontFamily(def, "SansSerif");

            Style s = doc.addStyle("italic", regular);
            StyleConstants.setItalic(s, true);

            s = doc.addStyle("bold", regular);
            StyleConstants.setBold(s, true);

            s = doc.addStyle("small", regular);
            StyleConstants.setFontSize(s, 10);

            s = doc.addStyle("large", regular);
            StyleConstants.setFontSize(s, 16);

            s = doc.addStyle("icon", regular);
            StyleConstants.setAlignment(s, StyleConstants.ALIGN_CENTER);
            ImageIcon pigIcon = createImageIcon("images/Pig.gif",
                    "a cute pig");
            if (pigIcon != null) {
                StyleConstants.setIcon(s, pigIcon);
            }

            s = doc.addStyle("button", regular);
            StyleConstants.setAlignment(s, StyleConstants.ALIGN_CENTER);
            ImageIcon soundIcon = createImageIcon("images/sound.gif",
                    "sound icon");
            JButton button = new JButton();
            if (soundIcon != null) {
                button.setIcon(soundIcon);
            } else {
                button.setText("BEEP");
            }
            button.setCursor(Cursor.getDefaultCursor());
            button.setMargin(new Insets(0, 0, 0, 0));
            button.setActionCommand(buttonString);
            button.addActionListener(this);
            StyleConstants.setComponent(s, button);
        }

        protected ImageIcon createImageIcon(String path,
                String description) {
            java.net.URL imgURL = TextSamplerDemo.class.getResource(path);
            if (imgURL != null) {
                return new ImageIcon(imgURL, description);
            } else {
                System.err.println("Couldn't find file: " + path);
                return null;
            }
        }
    }
}

为了实现这一点,您需要复制面板最初包含的所有数据,否则它就会毫无意义

答案 1 :(得分:0)

我设法通过使用

modyfying TmpPanel构造函数来解决它
public TmpPanel()
{
    setContentPane(panel1);
    pack();
    dispose();
}

现在isValid已为所有组件返回true。每次按下printButton时,都会创建一个带有printPanel的新的不可见JFrame,但我dispose它会立即生成,因此没有桌面空间也不会使用额外的内存。我现在可以轻松打印

printComponent(tmp.getPrintablePanel());