Tabla de Contenidos
Gráficos e informes
Trabajar con imágenes
Para trabajar con imágenes en Java tenemos diferentes clases. A continuación se comentan algunas clases / interfaces:
La clase principal es java.awt.Image. Image
es una clase abstracta por lo que no se puede instanciar, y además es la superclase de todas las clases que representan imágenes gráficas. Si queremos redimensionar imágenes, crear ficheros de imágen, poder modificarlas, etc, necesitamos trabajar con esta clase. La clase concreta BufferedImage
es una subclase de Image
.
Por otro lado tenemos la posibilidad de mostrar iconos en algunos componentes de Swing como son los JButtons
, JLabels
, JMenuItems
, JTabbedPane
, etc. Para este tipo de elementos debemos usar alguna clase que implemente la interface Icon.
La clase ImageIcon es una implementación de la interface Icon
que nos permite pintar iconos a partir de archivos de imágen jpg, gif, png, etc. Es la clase que utilizaremos principalmente.
Por último, la clase ImageIO contiene una serie de métodos estáticos de utilidad para codificar o decodificar imágenes desde diferentes fuentes.
Mostrar imágenes
Para mostrar una imagen necesitamos un contenedor para emplazarla. La forma más sencilla es utilizar una JLabel
y añadirle un elemento ImageIcon
con nuestra imágen:
JLabel lblFoto = new JLabel(); lblFoto.setIcon(new ImageIcon("foto1.jpg")); //Ó si tenemos la imagen referenciada mediante un objeto File File foto = new File("foto1.jpg"); //Ó File foto = fileChooser.getSelectedFile(); lblFoto.setIcon(new ImageIcon(foto.getPath()));
En el ejemplo anterior podemos utilizar un componente JFileChooser
para seleccionar la foto y usarla en la etiqueta mediante el objeto File
que nos devuelve el selector (getSelectedFile()
).
Redimensionar imágenes
Cuando mostramos una imagen en un JLabel
la imagen no se redimensiona. Pueden ocurrir dos cosas:
- La etiqueta tiene un tamaño fijo, por lo que se mostrará solo una parte de la foto.
- La etiqueta no tiene tamaño, por lo que la etiqueta será tan grande como la foto.
Tanto de una forma como de otra, no nos interesa tener una etiqueta muy grande en nuestra ventana, ni perder una parte de la foto por no entrar en las dimensiones de la etiqueta. Por ello debemos redimensionar la foto al tamaño de la etiqueta, ó usar un tamaño por defecto para la foto para que la etiqueta se ajuste a él.
Para redimensionar una imagen no nos basta con la clase ImageIcon
, ya que no permite esas operaciones. Necesitamos trabajar con la clase Image
. En el ejemplo siguiente vemos los pasos necesarios:
//1- cargamos la imagen desde su ruta ImageIcon imagenIcono = new ImageIcon("foto.jpg"); //ó desde JFileChooser ImageIcon imagenIcono = new ImageIcon(fileChooser.getSelectedFile().getPath()); //2- despues la transformamos a objeto Image Image imagen = imagenIcono.getImage(); //3- creamos un nuevo objeto Image redimensionado int ancho = 60; int alto = 70; Image imagenEscalada = imagen.getScaledInstance(ancho, alto, Image.SCALE_SMOOTH); //4- creamos el icono a partir de Image imagenIcono = new ImageIcon(imagenEscalada); //Ahora lo mostramos en la etiqueta vista.labelFoto.setIcon(imagenIcono); //También lo podemos hacer en menos pasos ImagenIcon icono = new ImageIcon(fichero); icono.setImage(icono.getImage().getScaledInstance(ancho, alto, Image.SCALE_SMOOTH));
Mantener la relación adecuada
Si hemos probado a escalar imágenes según el ejemplo anterior, no habremos dado cuenta que si las imágenes mantienen la misma relacion ancho/alto siguen teniendo el mismo aspecto. El problema se da cuando las imágenes que queremos redimensionar son mas anchas que altas, o viceversa.
Para ello a la hora de redimensionar debemos calcular la proporción exacta. Básicamente tenemos que aplicar una fórmula matemática:
//Tenemos una imagen y queremos escalarla a unas proporciones (60x70) ImagenIcon icono = new ImageIcon(fichero); int anchoFinal = 60; int altoFinal = 70; //Obtengo las dimensiones del icono int altoOriginal = icono.getIconHeight(); int anchoOriginal = icono.getIconWidth(); //calcular relación de ancho y de alto double relacionAncho = (double)anchoFinal / anchoOriginal; double relacionAlto = (double)altoFinal / altoOriginal; //Debo aplicar la menor de las relaciones double relacion = Math.min(relacionAlto, relacionAncho); int anchoRecomendado = (int)(anchoOriginal * relacion); int altoRecomendado = (int)(altoOriginal * relacion); //Escalo la imágen a las proporciones obtenidas icono.setImage(icono.getImage().getScaledInstance(anchoRecomendado, altoRecomendado, Image.SCALE_SMOOTH));
La explicación de las operaciones anteriores es la siguiente: Si tengo una imagen de 50×50 y quiero reducirla a 25×25, la tasa de escalado es 25/50, osea un 50% ó 0,5 de las dimesiones originales. Pero si la imagen es 70×40, y quiero escalarla a 25×25, necesito saber si reduciendo el alto, el ancho entrará, ó viceversa. Por eso debo calcular las dos relaciones, y saber cuál es la menor. Esa tasa será la que debo utilizar.
Guardar imágenes
Si utilizamos la clase ImageIcon
para representar imágenes, guardar estas imágenes justo con los demás datos no supone mayor problema. ImageIcon
implementa la interface Serializable
por lo que podemos guardar ese tipo de datos junto con el resto de objetos.
Guardar archivos de imágenes
Aunque hemos visto una forma de trabajar con imágenes dentro de nuestra aplicación, tiene un inconveniente principal: las imágenes guardadas como objetos ImageIcon no podemos reutilizarlas o guardarlas en el equipo. Además son objetos que ocupan bastante memoria.
La mayoría de aplicaciones que trabajan con imágenes importadas por el usuario, guardan una copia de esas imágenes en el árbol de directorios de la aplicación. Por ejemplo es bastante habitual el uso de imágenes thumbnails que son imágenes tamaño icono que sirven para previsualizar una imagen mayor, a la que podremos acceder posteriormente.
Si solo tenemos una imágen en un tamaño muy grande, al programa le costará cargarla, por lo que es útil tener distintas versiones de una misma imagen dentro de mi programa.
Esto solo se puede hacer guardando la imágenes importadas por el usuario en algún directorio de nuestra aplicación.
Copiar imágenes
Si un usuario importa una imágen a nuestro programa desde una ruta específica de su ordenador, nos interesa gurdar dentro de los directorios de nuestro programa esa imágen, por que la localización de la imagen original puede ser borrada en cualquier momento, y no queremos que nuestro programa pierda esa imágen.
La clase Files
nos permite copiar archivos de forma bastante sencilla. Además la clase File
nos permite crear directorios o comprobar si ya existen:
File origen = new File(...); File destino = new File(...); //Copia el fichero y sustituye si ya existe Files.copy(origen.toPath(), destino.toPath() , StandardCopyOption.REPLACE_EXISTING);
Además si queremos crear un directorio para almacenarlas, o comprobar si ya existe:
File directorio = new File("images"); directorio.mkdir(); //o crea la ruta completa si no existen los directorios padre File directorio = new File("images/thumbs"); directorio.mkdirs(); //También puedo comprobar si ya existe ese directorio File directorio = new File("images"); if(!directorio.exists()){ directorio.mkdir(); }
Crear nuevos archivos de imágenes
Como hemos dicho puede que nos interese a la hora de importar una imágen, aparte de guardar una copia en nuestra estructura de directorios puede que nos interese también guardar un archivo con la imagen redimensionada: un thumbnail o una imágen de icono para un ListCellRenderer. Si tenemos una imagen en un tamaño muy amplio, debemos de crear una nueva imágen. Con el siguiente método podemos realizar esa función, empleando las clases BufferedImage
, Graphics2d
y ImageIO
:
public static void copiarFotoRedimensionada(File inputFile, File outputFile , int anchoFinal, int altoFinal) throws IOException { // Leemos el archivo de imagen BufferedImage inputImage = ImageIO.read(inputFile); //calcular proporcion correcta int altoOriginal = inputImage.getHeight(); int anchoOriginal = inputImage.getWidth(); double proporcionAncho = (double)anchoFinal / (double)anchoOriginal; double proporcionAlto = (double)altoFinal / (double)altoOriginal; double proporcion = Math.min(proporcionAlto, proporcionAncho); int anchoRecomendado = (int)(anchoOriginal * proporcion); int altoRecomendado = (int)(altoOriginal * proporcion); // Creamos el objeto para la nueva imagen BufferedImage outputImage = new BufferedImage(anchoRecomendado , altoRecomendado , inputImage.getType()); // Escalamos la imagen Graphics2D g2d = outputImage.createGraphics(); g2d.drawImage(inputImage, 0, 0, anchoRecomendado, altoRecomendado, null); g2d.dispose(); // Escribimos en fichero ImageIO.write(outputImage, "png", outputFile); }
Gráficas: Libreria JFreeChart
JFreeChart es una librería de código abierto para generar distintos tipos de gráficas en Java.
Su código pueden ser descargadas desde la sección descargas de su página web, o descargar las librerías desde el repositorio de Maven.
Dependencias
Si buscamos la librería en el repositorio de Maven podemos ver sus dependencias. Las dependencias son las librerías secundarias que necesita una librería concreta para funionar. Es habitual que para utilizar una librería concreta se necesiten otras librerías de terceros.
Para utilizar JFreechart necesitamos incluir en el classpath de nuestro proyecto la librería y sus dependencias:
Para las versiones 1.0.X, JFreeChart necesita las librerias JCommon.
jfreechart-1.0.X.jar
jcommon-1.0.X.jar
Actualmente se ha distribuido la versión 1.5.X de JFreeChart, la cual no conlleva dependencias para el trabajo que vamos a realizar con ella, aunque sigue siendo recomendable incluir JCommon, si vamos a añadir gráficos a nuestros informes Jasper Report
Los siguientes ejemplos de código están planteados con las versiones: jfreechart-1.0.19 y jcommon-1.0.24
Funcionamiento
Posteriormente para crear un diagrama utilizaremos:
- Un Dataset: estructura de datos que almacena los datos mostrados en el diagrama. Hay diferentes dataset dependiendo del tipo de diagrama que necesitemos.
- Clase
ChartFactory
: dependiendo del tipo de diagrama usaremos un método. Todos los métodos devuelven un objetoJFreeChart
. - Clase
JFreeChart
: representa el diagrama generado, y lo podemos desplegar en un JFrame, en un JPanel, exportar en imágen, etc.
La documentación de la API de JFreeChart la tenemos en el siguiente enlace http://www.jfree.org/jfreechart/api/javadoc/org/jfree/chart/package-summary.html
//Creamos un dataset DefaultPieDataset dataset = new DefaultPieDataset(); //poblamos el dataset . . . //Creamos el diagrama a partir de ese dataset poblado JFreeChart diagrama = ChartFactory.createPieChart(dataset)
Gráficas de sectores
Es el tipo de gráfica más sencilla de generar. Necesitamos indicar los elementos que queremos mostrar en la gráfica, y sus valores.
Al método setValue()
se le pasan dos parámetros: la clave indicará el nombre de cada uno de los sectores, y el valor indicará el area de cada sector.
DefaultPieDataset dataset = new DefaultPieDataset(); dataset.setValue("Alvaro", 3); dataset.setValue("Fernando", 1); dataset.setValue("María", 2); JFreeChart grafica = ChartFactory.createPieChart("Vehiculos por conductor", dataset); //También existe el diagrama en 3D JFreeChart grafica = ChartFactory.createPieChart3D("Vehiculos por conductor", dataset);
Existe otra versión también, en forma de anillo. Los 3 últimos parametros indican: leyenda(true), tooltips(true) y url(false), en caso de que se muestra en una web.
JFreeChart grafica = ChartFactory.createRingChart("Vehiculos por conductor" , dataset, true, true, false);
Diagrama PieChart3D y RingChart
Gráficas de barras
Podemos crear diferentes tipos de gráficas de barras. Con una columna por cada clave, o tambien varias columnas.
Su dataset es DefaultCategoryDataset
.
El método setValue()
en este caso recibe tres parámetros:
- El valor indicará la longitud de las barras en el gráfico de barras.
- El nombre de la empresa. Cada nombre distinto que pongamos, dará lugar a una barra de distinto color. En nuestro caso, si tenemos dos empresas, tendremos barras de dos colores por cada accionista
- El nombre del accionista del que se miden la cantidad de acciones. Cada nombre distinto dará lugar a una nueva barra.
DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.setValue(20, "Facebook", "Pedro"); dataset.setValue(89, "Facebook", "Ana"); dataset.setValue(34, "Facebook", "Sofia"); JFreeChart grafica = ChartFactory.createBarChart("Acciones por cliente", "Accionista", "Cantidad de acciones",dataset);
BarChart de una sola columna
También podemos indicar distintas categorías para cada clave de la barra horizontal.
DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.setValue(20, "Facebook", "Pedro"); dataset.setValue(67, "Amazon", "Pedro"); dataset.setValue(54, "Amazon", "Ana"); dataset.setValue(89, "Facebook", "Ana"); dataset.setValue(13, "Amazon", "Sofia"); dataset.setValue(34, "Facebook", "Sofia"); JFreeChart grafica = ChartFactory.createBarChart("Acciones por cliente", "Accionista", "Cantidad de acciones",dataset); //O una misma barra con dos colores por accionista JFreeChart grafica = ChartFactory.createStackedBarChart("Acciones por cliente", "Accionista", "Cantidad de acciones",dataset);
StackedBarChart3D y BarChart, con dos columnas
Los diagramas de barras también tienen su versión 3D.
Más tipos de diagramas
Con las mismas clases que construimos un diagrama de barras podemos crear otros diagramas. Para los ejemplos, los datos reflejan el número de visitas por cada día de la semana a dos sitios web:
DefaultCategoryDataset dataset = new DefaultCategoryDataset(); dataset.setValue(345, "Facebook", "Lunes"); dataset.setValue(142, "Facebook", "Martes"); dataset.setValue(34, "Facebook", "Miercoles"); dataset.setValue(456, "Facebook", "Jueves"); dataset.setValue(200, "Facebook", "Viernes"); dataset.setValue(123, "Amazon", "Lunes"); dataset.setValue(345, "Amazon", "Martes"); dataset.setValue(678, "Amazon", "Miercoles"); dataset.setValue(765, "Amazon", "Jueves"); dataset.setValue(456, "Amazon", "Viernes");
Diagrama de Area
Muestra tantas areas coloreadas como claves haya. Similar al diagrama de columnas. En este caso el StackedAreaChart es el único que muestra los datos de las dos claves, ya que en el AreaChart estandar, un area oculta a la otra:
//AreaChart ChartFactory.createAreaChart("Visitas por dia", "Dias", "Visitas", dataset); //StackedAreaChart ChartFactory.createStackedAreaChart("Visitas por dia", "Dias", "Visitas", dataset);
Diagrama StackedAreaChart
Diagrama de Línea
Muestra un diagrama de líneas. También permite crear una versión 3D
ChartFactory.createLineChart("Visitas por dia", "Dias", "Visitas", dataset); //En 3D ChartFactory.createLineChart3D("Visitas por dia", "Dias", "Visitas", dataset); //Podemos también, como en otros diagramas, mostr el PLOT en horizontal ChartFactory.createLineChart3D("Visitas por dia", "Dias", "Visitas", dataset , PlotOrientation.HORIZONTAL, true, true, false);
Diagrama LineChart3D
Diagrama de Sectores Múltiple
Usando el objeto DefaultCategoryDataSet, crea un conjunto de diagramas de sectores. Podemos organizarlo por filas, o por columas (Webs, ó día de la semana):
ChartFactory.createMultiplePieChart("Visitas por dia", dataset , TableOrder.BY_ROW, true, true, false); ChartFactory.createMultiplePieChart("Visitas por dia", dataset , TableOrder.BY_COLUMN, true, true, false);
Diagrama MultiplePieChart por filas (pagina web)
Diagrama MultiplePieChart por columnas (día de la semana)
Diagrama de Cascada
Es como un diagrama de barras, pero las siguientes continúan donde terminan las anteriores:
ChartFactory.createWaterfallChart("Visitas por dia", "Dias", "Visitas", dataset , PlotOrientation.HORIZONTAL,true, true, false);
Diagrama WaterfallChart en horizontal
Desplegar diagrama
Hemos visto en los ejemplos que cualquier diagrama corresponde a una instancia de la clase JFreeChart
. Una vez que tenemos dicho objeto, tenemos diferentes formas de exportarlo o mostrarlo.
Como detalle, un diagrama JFreeChart esta compuesto dos areas principales: el area del panel contenedor y el area del diagrama (Plot):
JFreeChart grafica; . . . grafica.setBackgroundPaint(Color.cyan.brighter()); grafica.getPlot().setBackgroundPaint(Color.green);
Como hemos visto en ejemplos anteriores, el area del Plot puede tener orientación vertical u horizontal: PlotOrientation.HORIZONTAL / VERTICAL
.
Mediante una ventana JFrame
La clase ChartFrame
, que hereda de JFrame
, permite crear una ventana que contiene únicamente el diagrama. Al constructor debemos pasarle el objeto JFreeChart
con el diagrama:
Chartframe ventana = new ChartFrame("Titulo de la ventana", diagrama); //La sentencia pack() ajusta el JFrame a su contenido //Si queremos dar un tamaño concreto usamos ventana.size(400, 300) en lugar de pack() ventana.pack(); ventana.setVisible(true); //La centro en la pantalla ventana.setLocationRelativeTo(null);
Mediante un JPanel
La clase ChartPanel
que hereda de JPanel, crea un panel que contiene el diagrama. Este panel se lo podemos añadir a cualquier ventana de nuestra aplicación:
ChartPanel panel = new ChartPanel(diagrama); //Posteriormente podemos añadirlo a otro componente (panel, ventana, etc) JDialog dialogo = new JDialog(); dialogo.getContentPane().add(panel); dialogo.pack(); dialogo.setVisible(true);
También podriamos crear una misma ventana donde mostrar el diagrama deseado pulsando diferentes botones.
Exportar a fichero
Podemos crear un fichero de imágen jpg ó png con la gráfica, usando los métodos estáticos de la clase ChartUtilities
:
ChartUtilities.saveChartAsJPEG(File destino, JFreeChart diagrama, int ancho, int alto)
ChartUtilities.saveChartAsPNG(File destino, JFreeChart diagrama, int ancho, int alto)
File fichero = new File("imagen"); ChartUtilities.saveChartAsJPEG(fichero, diagrama, 500, 500); ChartUtilities.saveChartAsPNG(fichero, diagrama, 500, 500);
Informes: Librería JasperReport
JasperReports es una la librería para generar informes más usada a nivel mundial. Es un producto de código abierto, está escrita en Java, y permite mostrar informes desde una aplicación, así como exportarlos en mutiples formatos (PDF, HTML, Word, Excel, etc).
Esta herramienta funciona del siguiente modo:
- Crear una plantilla para informe: la plantilla del informe es un fichero XML (.jrxml) en el que se indica el aspecto que tendrá el informe, y los campos donde indicaremos nuestros valores.
- Compilar el informe: una vez que compilamos la plantilla .jrxml obtenemos un fichero de informe .jasper. No es necesario compilar el informe cada vez que queremos mostrarlo, solo cuando realicemos cambios.
- Rellenar el informe mediante una fuente de datos: de un modo similar a los dataset para diagramas, JasperReports necesita un conjunto de datos para poblar el informe. Con ello obtenemos un objeto
JasperPrint
. Puede ser a través de una bbdd o mediante una colección de datos de nuestro programa. - Distruibuir el informe: Mediante el objeto
JasperPrint
podemos cargarlo en un visor mediante herramientas de JasperReport o exportarlo a fichero en un formato concreto.
Diseñar informes con Jaspersoft Studio
Es la herramienta ofrecida por JasperSoft para el diseño de informes. Básicamente es un IDE, basado en eclipse, centrado en el diseño de los informes JasperReport.
Proceso de diseño de informes
JasperReport permite obtener la fuente de datos desde una base de datos. Esta forma de trabajar facilita mucho las cosas, ya que nos permite conectar con la base de datos desde JasperSoft Studio, y compilar el informe mostrando los datos.
En este caso crearemos un informe sin usar un DataSource desde una base de datos.
- Desde el IDE JasperSoft Studio crearemos un nuevo proyecto
- Dentro de ese proyecto crear un JasperReport
- Podemos utilizar una plantilla para crearlo
- En la sección de DataSource, debemos indicar “one empty record”
- Finalizar
Una vez hecho esto tenemos la plantilla del informe:
Click sobre la imagen para abrirla en una pestaña
Ahora debemos diseñar el informe. Para ello utilizaremos elementos de la paleta (rodeada en color azul) para añadir las cabeceras de los campos que deseo mostrar.
Posteriormente debo añadir los campos que quiero mostrar. Si quiero mostrar un informe con los Vehiculos{matricula, modelo, kms, fechafabricacion, conductor} que almacena mi aplicación en una estructura de datos debo conocer el nombre y tipo de los atributos de la clase Vehiculo:
- Por cada atributo de la clase que quiera mostrar, debo crear un campo (Field).
- Debe tener el mismo nombre y ser del mismo tipo de datos que se indica en la clase.
- Arrastro los campos (Field) a la plantilla, en el lugar en el que quiero que sean mostrados.
Una vez que tengo mi informe diseñado, pulso sobre el botón compilar, para obtener el archivo .jasper.
Compilar informes
Como hemos visto, con el IDE JasperSoft Studio podemos compilar los informes .jrxml y obtener directamente el fichero .jasper. Pero también podemos compilar el fichero .jrxml desde java:
Supongamos que tenemos el informe sin compilar en un directorio de recursos llamado “reports”:
JasperReport report = JasperCompileManager.compileReport("reports/Informe.jrxml"); //También podemos compilar y generar el fichero jasper (se crea en la misma ruta) JasperCompileManager.compileReportToFile("src/reports/Informe.jrxml"); //Indicando una ruta específica JasperCompileManager.compileReportToFile("reports/Informe.jrxml" , "reportsCompilados/Informe.jasper");
Rellenar informe con datos JavaBean
Como hemos dicho anteriormente, vamos a crear informes sin utilizar un DataSource proveniente de una base de datos, sino que los datos del informe provienen de una Colección de datos en mi aplicación. La clase que necesitamos usar ese JRBeanCollectionDataSource
:
- Lo primero que debemos hacer es obtener el objeto
JasperReport
a partir del fichero .jasper. - Después crearemos una instancia de
JRBeanCollectionDataSource
a partir de nuestra colección de datos. Cualquier estructura de datos de Java implementaCollection
(ArrayList, LinkedList, HashSet, HashMap, TreeMap, etc) - Mediante la clase
JasperFillManager
rellenamos el informe. El métodofillReport()
, recibe 3 parámetros: el informe, un HashMap con los parámetros (opcional) y la coleccion con los datos.
//En este caso utilizamos una colección de tipo HashSet HashSet<Vehiculo> vehiculos = obtenerVehiculos(); JasperReport report = (JasperReport) JRLoader.loadObject(new File("informes/Vehiculos.jasper")); JRBeanCollectionDataSource coleccion = new JRBeanCollectionDataSource(vehiculos); JasperPrint jasperPrint = JasperFillManager.fillReport(report, null, coleccion);
Visualizar el informe
Para visualizar el informe necesito una instancia de JasperPrint
, obtenida después de poblar el informe.
La clase JRViewer
extiende de JPanel
y provee un panel para visualizar informes, imprimirlos y guardarlos a fichero. Ese panel se puede añadir a una ventana o a otro panel en una sección de alguna ventana de mi programa.
JasperPrint jasperPrint = obtenerJasperPrint(); JRViewer visor = new JRViewer(jasperPrint); //Creo un JDialog para mostrar el informe JDialog dialog = new JDialog(); dialog.getContentPane().add(visor); dialog.setDimesion(700,700); dialog.setVisible(true);
Por otro lado puedo emplear la clase JasperViewer
que extiende de JFrame
y muestra una ventana con los mismos controles que hemos visto en el ejemplo anterior. Podemos crear una isntancia mediante el constructor, o mediante un método estático.
//Mediante método estático (false para no terminar la aplicación) JasperViewer.viewReport(jasperPrint, false); //ó creando una instancia JasperViewer visor = new JasperViewer(jasperPrint, false); visor.setVisible(true);
Exportar Informes
Desde el visor JasperViewer se permite guardar directamente el informe en multiples formatos, pulsando el boton Guardar.
En caso de que no queramos visualizar el informe desde nuestra aplicación sino simplemente guardarlo en fichero, la clase JasperExportManager
contiene métodos para exportar a distintos formatos. Aquí se encuenta la documentación de la clase.
JasperPrint jprint = obtenerJasperPrint(); JasperExportManager.exportReportToPdfFile(jprint, "rutaFichero"); JasperExportManager.exportReportToHtmlFile(jprint, "rutaFichero"); JasperExportManager.exportReportToXmlFile(jprint, "rutaFichero", false);
Personalizar los datos del informe
A la hora de diseñar el informe, además de añadir los campos que quiero mostrar, puedo detallar la información concreta que quiero mostrar. Por ejmplo, si un campo corresponde a un tipo de datos del que quiero mostrar ciertas propiedades, no nos vale con mostrar el campo, sino que debo diseñar una expresión concreta para mostrar la información. Lo mismo ocurre cuando quiero mostrar imágenes contenidas en los objetos de mi colección.
Para ello hago uso del diseñador de expresiones de JasperSoft Studio.
Vista del diseñador de expresiones
Ejemplo completo con IntelliJ
En el siguiente video realizado desde la perspectiva del IDE IntelliJ se realiza:
- Diseño de informe
- Creación de librerias de clases desde IntelliJ
- Utilización de esas librerías en JasperSoftStudio
- Crear gráfico JFreeChart en Informe
- Rellenar Informe
- Mostrar informe
Subinformes con JasperReport
Hasta ahora hemos visto como mostrar informes en los que se accede a algún atributo de una clase creada por nosotros. Los subinformes se utilizan cuando dentro de las filas de un informe, deseo incluir una colección indefinida de datos.
En el siguiente video se diseña un informe que muestra los datos de una colección de asignaturas, y emplea un subinforme para mostrar también los alumnos matriculados en cada asignatura:
Dependencias de JasperReport
Como es habitual en desarrollo, alguas librerías de Java dependen de otras tecnologías de terceros. En el caso de JasperReport necesitamos algunas librerias externas.
En el repositorio de Maven podemos ver las dependencias de cada versión de JasperReports.
Para la última versión (6.16.0) las dependencias que necesitamos para trabajar son:
- Apache Commons Logging: sitio de Apache o repositorio de Maven. Se utiliza para guardar trazas de log.
- Apache Commons Digester 2.1: sitio de Apache o repositorio de Maven. Se usan para leer ficheros de configuración XML.
- Apache Commons Collections: sitio de Apache o repositorio de Maven. Para trabajar con datasources de colecciones. No necesario si genero informes desde una bbdd.
- Apache Commons BeanUtils: sitio de Apache o repositorio de Maven. Para acceder dinámicamente a las propiedades de los objetos Java Beans.
Con las 4 anteriores podemos desplegar informes. Pero para guardarlos en diferentes formatos necesitamos resolver algunas dependencias más:
- Itext PDF Library: repositorio de Maven. Se usa para exportar a PDF.
- Apache POI: sitio de Apache o repositorio de Maven. API para los documentos de Microsoft, en este caso para exportar a Excel. La versión 4.1.0 no tiene más dependencias, que si tiene la última versión.
Indicar las rutas de ficheros de recursos
Cuando indicamos rutas de ficheros en nuestro rograma debemos de entender que las rutas que uso cuando desarrollo el proyecto (árbol de directorios del proyecto) no van a ser las mismas que cuando empaquete mi proyecto en un fichero ejecutable. Esta cuestión nos atañe siempre que utilicemos nuestras ficheros de imagen para decorar elementos de la GUI, y también en el caso de los ficheros Jasper de informes.
Para evitar los problemas que pueda originar el acceso a ficheros empleados en nuestro proyecto, Java tiene su propio sistema de acceso a recursos.
Cualquier recurso utilizado en nuestro proyecto debe estar en el ClassPath del proyecto. Ya lo hemos visto cuando importamos librerías de clases desde un fichero JAR externo. Cuando almacenamos ficheros en carpetas de recursos, el IDE las copia automáticamente al directorio bin del proyecto, preparándolos para su construcción (build) o para su empaquetado posterior.
Todo recurso que usamos en nuestro proyecto debe estar en un directorio de recursos.
Directorios de recursos en Eclipse
En eclipse basta con crear un package dentro del directorio src para albergar recursos.
Java utiliza la clase Class
y su método getResource()
para acceder a ficheros de recursos de forma independiente al lugar donde está el código fuente.
En los siguiente ejemplos tenemos la siguiente estructura de packages en Eclipse:
- src
- images
- foto.jpg
- reports
- Informe.jasper
- Informe.jrxml
//images en un package dentro de src JLabel lblFoto = new JLabel(); lblFoto.setIcon(new ImageIcon(getClass().getResource("/images/foto.jpg")); //reports es un package dentro de src JRLoader.loadObject(getClass().getResource("/reports/Informe.jasper")); //En caso de que el fichero deba ser leido, lo importamos como un Stream JasperCompileManager.compileReport(getClass().getResourceAsStream("/reports/Informe.jrxml"));
Si nos fijamos, las rutas indicadas en el método getResource()
, comienzan por una barra “/”, y continuan indicando la ruta de directorios de cada recurso.
Resources en IntelliJ
El IDE IntelliJ nos permite indicar los directorios que contienen recursos desde la pestaña File → Project Structure → Modules , tal y como vemos en la siguiente imagen:
Una vez que un directorio es marcado como directorio de recursos (resources), se incluye directamente en el classpath.
Una vez que hemos seleccionado un directorio como directorio de recursos, para utilizar los ficheros que contiene debemos indicar la ruta del fichero tomando como punto de partida dicho directorio.
Por ejemplo, el directorio de recursos images contiene la siguiente estructura:
- images (directorio)
- foto1.jgp (fichero)
- foto2.jpg (fichero)
- graficos (directorio)
- grafico1.jpg (fichero)
- grafico2.jpg (fichero)
Por otro lado tenemos otro directorio de recursos llamado reports:
- reports
- Informe.jasper
En los siguientes ejemplos se muestra como indicar la ruta de cada recurso:
//Para usar el fichero foto2.jpg label.setIcon(new ImageIcon(getClass().getResource("/foto2.jpg")); //Para usar el fichero grafico1.jpg label2.setIcon(new ImageIcon(getClass().getResource("/graficos/grafico1.jpg"))); //Para usar el informe Informe.jasper JRLoader.loadObject(getClass().getResource("/Informe.jasper"));
© 2024 Fernando Valdeón