java - 缩放 JComponent 以适合页面页面

标签 java swing intellij-idea printing

虽然我已经尽了最大的努力,但还是无法解决我的问题。问题是这样的:我想从我的框架中打印一个 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

绑定(bind)类:

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

现在我的问题如下:我想从 TmpPanel 打印 printPanel 以便它适合页面。以前在 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 背景的黑色矩形。

为什么即使这两个表单都是在设计器中创建的,又如何让它发挥作用?

最佳答案

因此,在我看来,问题的一部分是某些组件在附加到 native 对等点(或实现的窗口)之前不想渲染。

有两种选择,您可以简单地打印屏幕上已有的实时组件。这是一个问题,因为 ComponentPrintable 会改变组件的大小,这会影响 Activity 组件,让用户有些不舒服。

第二个选项是创建组件的新实例并将其放置在另一个 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;
            }
        }
    }
}

要使其真正发挥作用,您需要复制面板最初包含的所有数据,否则毫无意义

关于java - 缩放 JComponent 以适合页面页面,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35759522/

相关文章:

java - 强制 MobileFirst-CLI 使用 Java 6 的推荐方法?

java - 正则表达式匹配链接文本与 href 值不同的 anchor 标记

java - 如何将 intellij 的导入从内联更改为全局?

intellij-idea - 是否有: or ,或IntelliJ的plug-in对齐(我在Sublime Text中错过了它)?

java - 如何检查连接池是否被使用

java - 有什么方法可以替代使用 DOM XML 解析器对大型 XML 文档进行多个查找操作?

java - 删除 Eclipse 编译器出现的 "else" token

java - 如何更改(数据库)变量中的 jspinner 日期最小值和最大值?

java - mysql sqlException 错误

intellij-idea - 当 Intellij IDEA 未安装在 C 上时,sonarlint 不起作用 : drive in windows