调整JPanel的大小以准备打印而不将其从原始位置移除

在我的程序中,我经常需要打印各种JComponents(通常是JPanels),我喜欢它们是整页的。 我现在的方式是使用以下代码:

g2d.scale(pf.getImageableWidth()/componentToPrint.getWidth(), pf.getImageableHeight()/componentToPrint.getHeight()); 

但这通常会拉伸或以其他方式改变我想要打印的东西,我更喜欢做一些智能重新resize的东西,也许是一个function版本:

 componentToPrint.setSize(pf.ImageableWidth(), pf.ImageableHeight); 

或者说将组件添加到新的JFrame然后设置帧大小(问题是组件不能同时存在于两个地方)。 我不在乎resize是否会使GUI的其余部分看起来很糟糕,只要它是可以轻松重置的东西。

有没有办法做到这一点?

我认为你正在寻找的解决方案是构建一个包含所需内容的新JPanel,而不是打印副本。 如果你使用CellRendererPane这样做,你可以得到它听起来像你正在寻找的确切的resize行为。

如果你的JComponents编写得相当好,那么创建一个新的并且将其模型设置为与原始模型相同应该不是问题。

 CellRendererPane cellRendererPane = new CellRendererPane(); // It's important to add the cell renderer pane to something // you can use the same one for all of your exporting if you like and just // add it to your main frame's content pane - it won't show up anywhere. add(cellRendererPane); JPanel printPanel = createCopy(panel); cellRendererPane.paintComponent(g, printPanel, null, 0, 0, exportDim.width, exportDim.height, true); 

这是一个完整的工作示例。 createPanel()方法应该创建您想要渲染的任何组件。 一个真实的例子应该是确保使用相同的模型,而不是为一次性组件重新创建一个新模型。

 public class SCCE { public static void main(String[] args) throws Exception { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); final JFrame f = new JFrame("SCCE"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.getContentPane().setLayout(new BorderLayout()); f.getContentPane().add(SCCE.createPanel()); final CellRendererPane backgroundRenderer = new CellRendererPane(); // Add the renderer somewhere where it won't be seen f.getContentPane().add(backgroundRenderer, BorderLayout.NORTH); f.getContentPane().add(createSaveButton(backgroundRenderer), BorderLayout.SOUTH); f.pack(); f.setLocationRelativeTo(null); f.setVisible(true); } // Create your custom component from whatever model here.. private static final Component createPanel() { DefaultListModel model = new DefaultListModel(); for (int i = 0; i < 10; i++) { model.addElement("Item number " + i); } return new JList(model); } private static JButton createSaveButton(final CellRendererPane backgroundRenderer) { return new JButton(new AbstractAction("Save image to file") { @Override public void actionPerformed(ActionEvent e) { Dimension d = new Dimension(400, 300); BufferedImage img = new BufferedImage(d.width, d.height, BufferedImage.TYPE_INT_ARGB); Graphics2D g = img.createGraphics(); backgroundRenderer.paintComponent(g, createPanel(), null, 0, 0, d.width, d.height, true); g.dispose(); try { File output = new File("test.png"); System.err.println("Saved to " + output.getAbsolutePath()); ImageIO.write(img, "png", output); } catch (IOException ex) { ex.printStackTrace(); } } }); } } 

我认为你所面临的问题与规模操作无关。 这是你的逻辑不正确。 在大多数情况下,x和y刻度会有所不同,因为Imageable的刻度和组件的刻度将不同。 这是缩放操作后组件失真的原因。 你必须这样做:

 double factorX = pf.getImageableWidth() / component.getWidth(); double factorY = pf.getImageableHeight() / component.getHeight(); double factor = Math.min( factorX, factorY ); g2.scale(factor,factor); 

之后,您可以根据图像的新大小将图像转换为适当的坐标。 希望能帮助到你…

我会将JPanel实现的paintComponent()方法中的绘图代码重构为该面板中的public / package protected方法,该方法可以绘制任意宽度/高度的任意Graphics对象(可能是绘图代码是通用的)足够)。

这个例子有一个框架,包含一个面板,它有一些绘图逻辑(一个大的X,面板的大小)。 此示例的主要方法显示了一种获取图像并将其写入文件的方法,即使图像大小与面板大小不同也是如此。

 import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.image.BufferedImage; import java.io.File; import javax.imageio.ImageIO; import javax.swing.JFrame; import javax.swing.JPanel; public class MockFrame extends JFrame { // throws Exception, as just an example (not really advised to do this) public static void main(String[] args) throws Exception { MockFrame frame = new MockFrame(); frame.setVisible(true); // different sizes from the frame int WIDTH = 500; int HEIGHT = 500; BufferedImage b = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB); Graphics2D g2d = (Graphics2D) b.getGraphics(); // should set some background, as the panel's background // is dealt with by super.paintComponent() g2d.setBackground(Color.white); frame.getPanel().drawingLogic(b.getGraphics(), WIDTH, HEIGHT); ImageIO.write(b, "png", new File("test.png")); } private MockPanel panel; public MockFrame() { this.setSize(200, 200); this.setDefaultCloseOperation(EXIT_ON_CLOSE); panel = new MockPanel(); getContentPane().add(panel); } public MockPanel getPanel() { return panel; } private class MockPanel extends JPanel { @Override protected void paintComponent(Graphics g) { super.paintComponent(g); drawingLogic(g, getWidth(), getHeight()); } public void drawingLogic(Graphics g, int width, int height) { g.setColor(Color.black); g.drawLine(0, 0, width, height); g.drawLine(0, height, width, 0); } } } 

这允许GUI外部的对象挂钩到其绘图算法中。 我看到的一个缺点是,您将在任何想要打印面板的对象与面板的实际实现之间创建依赖关系。 但它仍然比在运行时调整面板大小更好(我已经尝试过并且似乎有一些问题 – 我认为setSize()更改需要一些时间才能传播。

编辑:

在回复评论时,我提供了上面代码片段的修改版本。 它可能不是最好的方法,并不是非常用户友好(所以我不会在最终用户应用程序中使用它),但它确实根据布局管理器的规则调整框架中的所有内容。

 /* This code snippet describes a way to resize a frame for printing at * a custom size and then resize it back. * * Copyright (C) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import java.awt.Container; import java.awt.Graphics2D; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; import java.awt.image.BufferedImage; import java.io.File; import javax.imageio.ImageIO; import javax.swing.JButton; import javax.swing.JFrame; import javax.swing.JOptionPane; public class MockFrame extends JFrame { // throws Exception, as just an example (not really advised to do this) public static void main(String[] args) throws Exception { final MockFrame frame = new MockFrame(); frame.setVisible(true); // different sizes from the frame final int WIDTH = 500; final int HEIGHT = 700; final BufferedImage b = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_ARGB); final Graphics2D g2d = (Graphics2D) b.getGraphics(); final int previousWidth = frame.getWidth(); final int previousHeight = frame.getHeight(); frame.setSize(WIDTH, HEIGHT); frame.repaint(); JOptionPane.showMessageDialog(null, "Press OK when the window has finished resizing"); frame.print(g2d); frame.setSize(previousWidth, previousHeight); ImageIO.write(b, "png", new File("test.png")); } public MockFrame() { this.setSize(200, 200); this.setDefaultCloseOperation(EXIT_ON_CLOSE); boolean shouldFill = true; boolean shouldWeightX = true; Container pane = getContentPane(); // code from // http://java.sun.com/docs/books/tutorial/uiswing/layout/gridbag.html // just to add some components in the frame... :) // left out in here for brevity } } 

代码基本上调整了框架的大小,向用户显示了一条确认消息,因此可以阻止线程直到重绘完成(可以通过Thread.sleep() ,但使用消息更透明)。 然后打印框架并将其重新调整为原始形状。 有点hacky,但它的工作原理。

-- Flaviu Cipcigan

我想你回答了自己的问题。 每个组件都有一个方法:

 setSize(int width, int height);