Contents

LibretaDirecciones: Gráficos e Informes

Gráficos

Ya que nuestra LibretaDirecciones incluye las fechas de nacimiento de cada persona, vamos a generar un informe que me permita mostrar en forma de gráfico de barras la cantidad de personas que nacen en cada mes del año.

La vista

Para ello, crearemos una nueva vista llamada VistaEstadisticas.fxml dentro del paquete view y, desde Scene Builder, le aplicaremos las siguientes características:
  • En la sección layout(derecha) del AnchorPane, establece Pref Width y Pref Height a 620x450.
  • Desde la sección Charts (izquierda) añade un BarChart dentro del AnchorPane.
  • Haz clic derecho sobre el BarChart y selecciona Fit to parent.
  • Guarda el archivo.

El controlador

Para controlar la vista, crea una nueva clase Java llamada VistaEstadisticasController dentro del paquete view con el siguiente código:

public class VistaEstadisticasController {

    @FXML
    private BarChart<String, Integer> graficoBarras;

    @FXML
    private CategoryAxis ejeX;

    @FXML
    private NumberAxis ejeY;

    private ObservableList<String> nombreMeses = FXCollections.observableArrayList();

    //Se invoca justo después de que se ha cargado el archivo FXML
    @FXML
    private void initialize() {

        //Array de nombre de meses
        String[] meses = {"Ene","Feb","Mar","Abr","May","Jun","Jul","Ago","Sep","Oct","Nov","Dic"};

        //Los convierto a lista obervable
        nombreMeses.addAll(Arrays.asList(meses));

        //Asigno los nombres de meses a categorías
        ejeX.setCategories(nombreMeses);

        //Etiquetas de los ejes
        ejeX.setLabel("Mes de nacimiento");
        ejeY.setLabel("Número de personas");
    }

    //Set mes de cada persona para el eje Y
    public void setDatosPersona(List<Persona> personas) {

        //Array con cantidad de personas por mes de nacimiento
        int[] numMes = new int[12];
        for (Persona p : personas) {
            int mes = p.getFechaDeNacimiento().getMonthValue() - 1;
            numMes[mes]++;
        }

        //Genero la serie
        XYChart.Series<String, Integer> series = new XYChart.Series<>();
        series.setName("Mes de nacimiento");
        for (int i = 0; i < numMes.length; i++) {
            series.getData().add(new XYChart.Data<>(nombreMeses.get(i), numMes[i]));
        }

        //Añado la serie al gráfico
        graficoBarras.getData().add(series);

    }

}
Como vemos, el controlador necesitará acceso a dos elementos de nuestro archivo FXML:
  • graficoBarras, que tiene los tipos String (meses en eje X) e Integer (número de personas en ese mes en el eje Y)
  • ejeX: Lo usaremos para añadir las nombres de los meses
El método initialize() rellena el eje X con la lista de todos los meses. El método setDatosPersona(...)será llamado por la clase LibretaDirecciones para establecer los datos de personas, recorriendo todas ellas y contando los nacimientos por mes, que luego añadirá a un nuevo XYChart.Data (una barra en el gráfico) para cada mes a la serie de datos. Para finalizar, añado la serie al gráfico.

Conexión de la vista a su controlador

Como en anteriores ocasiones, abre VistaEstadisticas.fxml en Scene Builder y realiza las siguientes acciones: En Controller (izquierda) selecciona VistaEstadisticasController como Controller class. En Hierarchy (izquierda), selecciona el BarChart y, en Properties(derecha) escribe fecha de nacimientocomo Title. En Hierarchy (izquierda), selecciona el BarChart y, en Code (derecha) asigna graficoBarras como fx:id. En Hierarchy (izquierda), selecciona CategoryAxis dentro de BarChart y, en Code (derecha) asigna ejecomo fx:id.

Conexión del controlador con la clase principal

Para el gráfico de estadísticas usaremos el mismo mecanismo que utilizamos para el diálogo de edición de personas, una ventana de diálogo emergente. Añade el siguiente método a tu clase LibretaDirecciones (declarando primero la variable vistaEstadisticas de tipo AnchorPane):

public void crearGrafico() {

        //Cargo la vista estadísticas
        FXMLLoader loader = new FXMLLoader();
        URL location = LibretaDirecciones.class.getResource("/view/VistaEstadisticas.fxml");
        loader.setLocation(location);
        try {
            vistaEstadisticas = loader.load();
        } catch (IOException ex) {
            Logger.getLogger(LibretaDirecciones.class.getName()).log(Level.SEVERE, null, ex);
        }

        //Inicializo un nuevo escenario y asigno el principal
        Stage escenarioEstadisticas = new Stage();
        escenarioEstadisticas.setTitle("Estadísticas");
        escenarioEstadisticas.initModality(Modality.WINDOW_MODAL);
        escenarioEstadisticas.initOwner(escenarioPrincipal);

        //Cargo la escena que contiene ese layout de estadisticas
        Scene escena = new Scene(vistaEstadisticas);
        escenarioEstadisticas.setScene(escena);

        //Asigno el controlador
        VistaEstadisticasController controller = loader.getController();
        controller.setDatosPersona(datosPersona);

        //Muestro el escenario
        escenarioEstadisticas.show();

    }

Asignar la acción al menú

En VistaPrincipal.fxml, añade un nuevo menú llamado Estadísticas con un MenuItem llamado Gráfico. En VistaPrincipalController, añade el siguiente método para controlar esa acción de menú:

    //Gráfico
    @FXML
    private void grafico() {
      libretaDirecciones.crearGrafico();
    }
Por último, en VistaPrincipal.fxml, selecciona el MenuItem Gráficode Estadísticas y, en Code (derecha), selecciona grafico para el campo On Action. Más información sobre gráficos con JavaFX AQUÍ.

Informes en PDF

Para generar un informe en PDF de nuestra libreta de direcciones, haremos uso de librería externa Apache PDFBox. Para ello, debemos acceder a la sección Downloads de la página y descargarnos el fichero pdfbox-app-2.0.13.jar o la versión más reciente que encontremos.

Instalación de la librería

Una vez descargada la librería, podemos añadirla a nuestro proyecto NetBeans en las Properties de nuestro proyecto (botón derecho), accediendo a la sección Libraries y añadiendo la ruta de nuestro archivo jar mediante el botón Add JAR/Folder..

Creación del método en la clase principal

De este modo, ya podremos crear un nuevo método en la clase principal LibretaDirecciones que genere un PDF básico con el contenido de nuestra libreta.

public void crearPDF() throws IOException{

        //Creo un nuevo documento, una página y la añado
        PDDocument documento = new PDDocument();
        PDPage pagina = new PDPage();
        documento.addPage(pagina);
        documento.getPage(0);

        //Inicio un nuevo stream de contenido
        PDPageContentStream contentStream = new PDPageContentStream(documento, pagina);

        //Establezco la posición Y de la primera líena y el tipo de fuente
        int linea = 700;
        contentStream.setFont(PDType1Font.TIMES_ROMAN, 12);

        //Recorro la lista de personas
        List<Persona> personas = datosPersona;
        for (Persona p : personas) {
            //Inicio un nuevo texto y escribo los datos
            contentStream.beginText();
                contentStream.newLineAtOffset(25, linea);
                contentStream.showText(p.getNombre()+" ");
                contentStream.showText(p.getApellidos()+" ");
                contentStream.showText(p.getDireccion()+" ");
            contentStream.endText();
            //Cambio de línea
            linea -= 25;
        }

        //Cierro el content stream
        contentStream.close();

        //INicio el file chooser
        FileChooser fileChooser = new FileChooser();

        //Filtro para la extensión
        FileChooser.ExtensionFilter extFilter = new FileChooser.ExtensionFilter(
                "PDF files (*.pdf)", "*.pdf");
        fileChooser.getExtensionFilters().add(extFilter);

        //Muestro el diálogo de guardar
        File archivo = fileChooser.showSaveDialog(getPrimaryStage());


        if (archivo != null) {

            //Me aseguro de que tiene la extensión correcta y si no la cambio
            String extension = "";
            if (!archivo.getPath().endsWith(extension)){
                extension = ".pdf";
            }
            //Escribo en el archivo y lo cierro
            archivo = new File(archivo.getPath() + extension);
            documento.save(archivo);
            documento.close();

        }

        //Abro el archivo en el visor de PDF del sistema
        if (Desktop.isDesktopSupported()) {
            try {
                Desktop.getDesktop().open(archivo);
            }
            catch (IOException ex) {
           }
        }

    }

Creación de la acción de menú en el controlador de la vista

Solo faltarían dos cosas: añadir un método para la acción del menú que generará un PDF en VistaPrincipalController:

//PDF
    @FXML
    private void pdf() throws IOException {
      libretaDirecciones.crearPDF();
    }

Conexión del controlador a la vista

Y añadir un MenuItem llamado PDF dentro del Menu Estadísticas al que le asignaremos como On Action el método pdf. El PDF aquí generado es bastante simple y muestra las opciones básicas. Puedes seguir ESTE TUTORIAL para generar PDFs más completos.