java - una - Cambio de tamaño de JPanel para prepararse para imprimir sin quitarlo de su posición original
setbounds jpanel (4)
En mi programa, frecuentemente necesito imprimir varios JComponents (generalmente JPanels) y me gusta que sean de página completa. La forma en que lo hago ahora es usando el siguiente código:
g2d.scale(pf.getImageableWidth()/componentToPrint.getWidth(), pf.getImageableHeight()/componentToPrint.getHeight());
pero esto a menudo alarga o deforma todo lo que estoy tratando de imprimir, y preferiría hacer algo que vuelva a clasificar según el tamaño de manera inteligente, tal vez una versión funcional de:
componentToPrint.setSize(pf.ImageableWidth(), pf.ImageableHeight);
o digamos agregar el componente a un nuevo JFrame y luego establecer el tamaño del marco (el problema es que los componentes no pueden existir en dos lugares a la vez). No me importaría si el cambio de tamaño haría que el resto de la GUI se viera terrible, siempre que sea algo que pueda restablecerse fácilmente.
¿Hay alguna manera de hacer esto?
Creo que el problema al que se enfrenta no tiene nada que ver con la operación de escala. Es tu lógica que es incorrecta. En la mayoría de los casos, sus escalas x e y serán diferentes, ya que la escala de Imageable y la escala de su componente no serán las mismas. Esta es la razón por la cual su componente está distorsionado después de la operación de escala. Tienes que hacer algo como:
double factorX = pf.getImageableWidth() / component.getWidth();
double factorY = pf.getImageableHeight() / component.getHeight();
double factor = Math.min( factorX, factorY );
g2.scale(factor,factor);
Después de eso, puede traducir su imagen a las coordenadas apropiadas según su nuevo tamaño. Espero eso ayude...
Creo que respondiste tu propia pregunta. Cada componente tiene un método:
setSize(int width, int height);
Refactorizaría el código de dibujo del método paintComponent()
de su implementación de JPanel
a un método protegido público / de paquete en ese panel que puede dibujar a un objeto Graphics
arbitrario, de cualquier ancho / alto (presumiblemente que el código de dibujo es general suficiente).
Este ejemplo tiene un marco, que contiene un panel, que tiene cierta lógica de dibujo (una X grande, el tamaño del panel). El método principal de este ejemplo muestra una forma de obtener una imagen y escribirla en un archivo, incluso si el tamaño de la imagen es diferente al tamaño del panel.
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);
}
}
}
Esto permite que los objetos externos a la GUI se enganchen en su algoritmo de dibujo. Una desventaja que veo es que creará una dependencia entre cualquier objeto que quiera imprimir el panel y la implementación real del panel. Pero aún es mejor que cambiar el tamaño del panel sobre la marcha (lo he intentado y parece haber algunos problemas, creo que lleva un tiempo que el cambio de setSize()
propague.
EDITAR:
En respuesta a los comentarios, proporcioné una versión modificada del fragmento de código anterior. Probablemente no sea la mejor manera de hacerlo, y no muy fácil de usar (por lo que no lo usaría en una aplicación de usuario final), pero cambia el tamaño de todo en el marco de acuerdo con las reglas del administrador de diseño.
/* 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
}
}
El código básicamente cambia el tamaño del marco, muestra al usuario un mensaje de confirmación para que el hilo pueda ser bloqueado hasta que se complete el repintado (podría hacerlo Thread.sleep()
, pero es más transparente usando un mensaje). Luego imprime el marco y lo redimensiona a su forma original. Un poco hacky, pero funciona.
-- Flaviu Cipcigan
Creo que la solución que está buscando sería construir un nuevo JPanel que contenga los contenidos deseados e imprimir la copia en su lugar. Si lo hace utilizando el CellRendererPane, puede obtener el comportamiento de cambio de tamaño exacto que suena como lo que está buscando.
Si sus JComponentes están razonablemente bien escritos, no debería ser un problema actualizar uno nuevo y establecer que su modelo sea el mismo que el del original.
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);
Aquí hay un ejemplo completo de trabajo. El método createPanel () debería crear cualquier componente que desee renderizar. Un ejemplo real debe ser seguro de usar el mismo modelo en lugar de volver a crear un nuevo modelo para un componente desechable.
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();
}
}
});
}
}