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:

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.

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:

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:

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:

  1. 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.
  2. 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.
  3. 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.
  4. 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.

  1. Desde el IDE JasperSoft Studio crearemos un nuevo proyecto
  2. Dentro de ese proyecto crear un JasperReport
  3. Podemos utilizar una plantilla para crearlo
  4. En la sección de DataSource, debemos indicar “one empty record”
  5. 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:

  1. Por cada atributo de la clase que quiera mostrar, debo crear un campo (Field).
  2. Debe tener el mismo nombre y ser del mismo tipo de datos que se indica en la clase.
  3. 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:

  1. Lo primero que debemos hacer es obtener el objeto JasperReport a partir del fichero .jasper.
  2. Después crearemos una instancia de JRBeanCollectionDataSource a partir de nuestra colección de datos. Cualquier estructura de datos de Java implementa Collection (ArrayList, LinkedList, HashSet, HashMap, TreeMap, etc)
  3. Mediante la clase JasperFillManager rellenamos el informe. El método fillReport(), 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:

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:

  1. Apache Commons Logging: sitio de Apache o repositorio de Maven. Se utiliza para guardar trazas de log.
  2. Apache Commons Digester 2.1: sitio de Apache o repositorio de Maven. Se usan para leer ficheros de configuración XML.
  3. Apache Commons Collections: sitio de Apache o repositorio de Maven. Para trabajar con datasources de colecciones. No necesario si genero informes desde una bbdd.
  4. 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:

  1. Itext PDF Library: repositorio de Maven. Se usa para exportar a PDF.
  2. 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:

//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:

Por otro lado tenemos otro directorio de recursos llamado reports:

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