miércoles, 29 de diciembre de 2021

Usando el dialogo de configuración de impresión

Tradicionalmente, los usuarios quieren ver la configuración de pagina y otras opciones importantes de impresión. Desde el dialogo de impresión tu puedes seleccionar una impresora, especificar las paginas a imprimir y configurar el número de copias.

Una aplicación muestra un dialogo de impresión cuando el usuario presiona el botón "Imprimir", o elige la opción "Imprimir" desde el menú de la aplicación. Para mostrar el dialogo, llamar al método printDialog de la clase PrinterJob.

Este método retorna true si el usuario presiona Aceptar en el dialogo, y false en caso contrario. Las opciones referentes al formato y numero de paginas están restringidas según lo que se estableció en la clase PrinterJob.

El método printDialog en la porción de código anterior abre un dialogo de impresión nativo.

Tu puedes cambiar la información de configuración de pagina que se encuentra en el objeto PageFormat mediante la configuración de pagina en el dialogo.
Para mostrar el dialogo de configuración de pagina, llamar al metodo pageDialog de la clase PrinterJob.

El dialogo de configuración de pagina es inicializado usando el parámetro que se le pasa al objeto pageDialog. Si el usuario hace click en el botón Aceptar del dialogo un objeto de la clase PageFormat se va a crear de acuerdo con las opciones seleccionadas por el usuario y luego este objeto será devuelto por el método. Si el usuario cancela el dialogo, el método pageDialog devuelve el objeto original PageFormat sin modificar.

Usualmente la API de impresión Java 2D requiere una aplicación para mostrar un dialogo de impresión, pero en ocasiones es posible imprimir sin mostrar ningún dialogo en absoluto. Este tipo de impresión es llamado impresión silenciosa. Esto puede ser practico en casos específicos, tales como cuando tu necesitas imprimir un reporte semanal particular de una base de datos. En otros casos siempre es recomendable informarle al usuario cuando un proceso de impresión está iniciando.

martes, 28 de diciembre de 2021

Un programa básico de impresión en Java

Esta sección explica como crear un programa básico de impresión que muestra un dialogo de impresión e imprime el texto "Hola Mundo" en la impresora seleccionada.

Imprimir un documento consiste en dos partes:

  • La clase Job para controlar la impresión - Crear una petición de impresión, asociar la petición con una impresora, especificar el número de copias, e interacción del usuario con el dialogo de impresión
  • Dibujando la página - Dibujar contenido en una página y administrar contenido que abarca páginas (paginación).

Primero crea una instancia de la clase Job. La clase que representa una petición de impresión y la mayoría de las otras clases relacionadas se encuentran en el paquete java.awt.print

A continuación proporciono el código que imprime en contenido en la pagina, esto se logra implementando la interfaz Printable.

Una aplicación por lo general mostrará un dialogo de impresión para que el usuario pueda ajustar varias opciones como son el numero de copias, la orientación de la pagina o seleccionar la impresora cuando tengo varias.

Este dialogo se muestra visible hasta que el usuario aprueba o cancela la impresión. La variable doPrint será true si el usuario confirmo que quiere continuar con la impresión.

Si la variable doPrint es falso, el usuario ha cancelado la impresión. Dado que mostrar el cuadro de dialogo es opcional, el valor devuelto es puramente informativo.

Si la variable doPrint es verdadero, entonces la aplicación solicitará que la impresión se realice, esto se concreta llamando al metodo print de clase PrinterJob.

La excepción PrinterException se lanzará si ocurre un problema al envíar nuestro trabajo a la impresora. Sin embargo, dado que el metodo PrinterJob.print retorna tan pronto como el trabajo ha sido enviado a la impresora, el usuario de la aplicación no puede identificar si no hay papel o si este se ha atascado. El control que proporciona la clase PrinterJob es suficiente para usos básicos de impresión.

La interfaz Printable tiene un solo metodo:

La clase PageFormat describe la orientación de la pagina (portrait o landscape), el tamaño del papel y el area donde se puede dibujar en unidades de 1/72 de una pulgada.

El área imprimible representa los límites de margen de la mayoría de las impresoras (margen del hardware). El area imprimible es el espacio dentro de estos margenes, y en la práctica, a menudo se limita aún más para dejar espacio para encabezados o pies de página.

El parámetro page en base zero representa el numero de pagina que será impreso.

El siguiente código representa una implementación completa de la clase Printable

Recuerda que la impresora tiene un margen definido y lo debes de considerar, por ello llamamos al método translate de la clase Graphics. Para conocer el tamaño del margen llamamos a los métodos getImageableX y getImageableY 

Enviar una instancia de la clase Graphics a la impresora es esencialmente lo mismo que renderizarla en la pantalla. En ambos casos, debe realizar los siguientes pasos:

  • Dibujar una cadena de texto de prueba es tan fácil como las otras operaciones que se describieron para dibujar usando la clase Graphics2D.
  • Los gráficos de la impresora tienen una resolución más alta, que debería ser transparente para la mayoría de los programas.
  • El método Printable.print() es llamado por el sistema de impresión al igual que se llama al método Component.paint() para pintar un componente en la pantalla. El sistema de impresión llamará al método Printable.print() para la pagina 0, 1, .. etc.
  • Hasta que el método print() devuelva NO_SUCH_PAGE.
  • Se puede llamar al método print() con el mismo índice de pagina varías veces hasta que se complete el documento. Esta función es aplicada cuando el usuario especifica atributos como múltiples copias con la opción collate.
  • El área imprimible de PageFormat determina el área del clip. El área imprimible también es importante para calcular la paginación o cómo distribuir el contenido en las páginas impresas, ya que los saltos de página están determinados por cuánto cabe en cada página.

Nota: Se puede omitir una llamada al método print () para ciertos índices de página si el usuario ha especificado un rango de página diferente que no involucra un índice de página en particular.

domingo, 19 de diciembre de 2021

Impresión en Java

La API Java 2D te permite dibujar en cualquier superficie, una extensión natural de eso es la habilidad de imprimir los gráficos Java 2D. Una impresora puede ser considerada un dispositivo grafico al igual que la pantalla.

La API de impresión Java 2D no está limitada a solo imprimir gráficos. Ya que también es posible imprimir el contenido de la interface de usuario de una aplicación. El contenido puede ser impreso enviando dato sin formato a la impresora delegando el formateo a la API de impresión Java 2D, o usando la API Graphics de Java 2D.

En esta lección vas a explorar las funciones de las clases printer y job que pertenecen a la API de impresión Java 2D y que son complementos de los elementos del renderizado. Aprenderás como buscar las impresoras configuradas en el sistema operativo o en la red y consultar información relevante sobre estas impresoras tales como el tamaño del papel soportado, seleccionar los parámetros de impresión y diálogos de usuarios.

Las principales clases e interfaces incluidas en la API de impresión se encuentran en los paquetes java.awt.print y javax.print (este último paquete te permite acceder a los servicios de impresión).

Las operaciones básicas de impresión son representados en las siguientes secciones:

  • Un programa básico de impresión - Esta sección describe la interface Printable y presenta un programa básico de impresión.
  • Usando el dialogo de parametros de impresión - Estás secciones explica como mostrar el dialogo Configuración de Impresión
  • Imprimiendo un documento con varías paginas - Esta sección explica como usar paginación para imprimir un documento con varias paginas.
  • Trabajando con servicios de impresión y atributos - Esta sección te enseña acerca de los servicios de impresión, como especificar el formato de impresión de los datos, y como crear una peticion de impresión usando el paquete javax.print.
  • Imprimiendo el contenido de una interfaz de usuario - Esta sección explica como imprimir el contenido de una ventana.
  • Soporte de impresión en componentes Swing - Esta sección proporciona una breve descripción de la funcionalidad de impresión en Swing y se refiere a clases e interfaces especificas de Swing


miércoles, 8 de diciembre de 2021

Genericos en Java

En cualquier proyecto de software no trivial, los errores son simplemente una realidad. Una planificación, programación y pruebas cuidadosas pueden ayudar a reducir su omnipresencia, pero de alguna manera, en algún lugar, siempre encontrarán una manera de introducirse en su código. Esto se vuelve especialmente evidente a medida que se introducen nuevas funciones y su código base crece en tamaño y complejidad.

Afortunadamente, algunos errores son más fáciles de detectar que otros. Los errores en tiempo de compilación, por ejemplo, se pueden detectar desde el principio; puede utilizar los mensajes de error del compilador para averiguar cuál es el problema y solucionarlo, en ese mismo momento. Los errores en tiempo de ejecución, sin embargo, pueden ser mucho más problemáticos; no siempre aparecen de inmediato, y cuando lo hacen, puede ser en un punto del programa que está muy alejado de la causa real del problema.

Los genéricos (o Generics) agregan estabilidad a su código al hacer que más errores sean detectables en el momento de la compilación.

¿Por qué utilizar genéricos?

En pocas palabras, los genéricos permiten que los tipos (clases e interfaces) sean parámetros al definir clases, interfaces y métodos. Al igual que los parámetros formales más familiares utilizados en las declaraciones de métodos, los parámetros de tipo proporcionan una forma de reutilizar el mismo código con diferentes entradas. La diferencia es que las entradas a los parámetros formales son valores, mientras que las entradas a los parámetros de tipo son tipos.

El código que usa genéricos tiene muchas ventajas sobre el código no genérico:

Comprobaciones de tipo más estrictas en tiempo de compilación.

Un compilador de Java aplica una fuerte verificación de tipos al código genérico y emite errores si el código viola la seguridad de los tipos. Arreglar errores en tiempo de compilación es más fácil que arreglar errores en tiempo de ejecución, que pueden ser difíciles de encontrar.

Eliminación de conversión de tipo (Cast).

El siguiente fragmento de código sin genéricos requiere conversión:

Cuando se reescribe para usar genéricos, el código no requiere conversión:

Permitir a los programadores implementar algoritmos genéricos.

Mediante el uso de genéricos, los programadores pueden implementar algoritmos genéricos que funcionan en colecciones de diferentes tipos, se pueden personalizar y son seguros para los tipos y más fáciles de leer.

Tipos genéricos

Un tipo genérico es una clase o interfaz genérica que se parametriza sobre tipos. La siguiente clase Box se modificará para demostrar el concepto.

Box, una clase simple

Comience examinando la clase Box no genérica que opera sobre objetos de cualquier tipo. Solo necesita proporcionar dos métodos: set, que agrega un objeto al cuadro, y get, que lo recupera:


Dado que sus métodos aceptan o devuelven un objeto, puede pasar lo que quiera, siempre que no sea uno de los tipos primitivos. No hay forma de verificar, en tiempo de compilación, cómo se usa la clase. Una parte del código puede colocar un número entero en el cuadro y esperar obtener números enteros, mientras que otra parte del código puede pasar por error una cadena, lo que da como resultado un error de tiempo de ejecución.

Una versión Generic de la clase Box

Una clase genérica se define con el siguiente formato:

class name<T1, T2, ..., Tn> { /* ... */ }

La sección de parámetro de tipo, delimitada por corchetes angulares (<>), sigue al nombre de la clase. Especifica los parámetros de tipo (también llamados variables de tipo) T1, T2, ... y Tn.

Para actualizar la clase Box para usar genéricos, cree una declaración de tipo genérico cambiando el código "Box de clase pública" a "Box de clase pública <T>". Esto introduce la variable de tipo, T, que se puede usar en cualquier lugar dentro de la clase.

Con este cambio, la clase Box se convierte en:


Como puede ver, todas las apariciones de Object se reemplazan por T. Una variable de tipo puede ser cualquier tipo no primitivo que especifique: cualquier tipo de clase, cualquier tipo de interfaz, cualquier tipo de arreglo o incluso otro tipo de variable.

Esta misma técnica se puede aplicar para crear interfaces genéricas.

Convenciones de nomenclatura de parámetros de tipo

Por convención, los nombres de los parámetros de tipo son letras mayúsculas simples. Esto contrasta fuertemente con las convenciones de nomenclatura de variables que ya conoce, y con una buena razón: sin esta convención, sería difícil distinguir entre una variable de tipo y un nombre de clase o interfaz ordinario.

Los nombres de parámetros de tipo más utilizados son:
  • E - Elemento (utilizado ampliamente por Java Collections Framework)
  • K - Clave
  • N - Número
  • T - Tipo
  • V - Valor
  • S, U, V, etc. - 2. °, 3. °, 4. ° tipos
Verá estos nombres utilizados en la API de Java SE y en el resto de esta lección.

Invocación y creación de instancias de un tipo genérico

Para hacer referencia a la clase Box genérica desde su código, debe realizar una invocación de tipo genérico, que reemplaza T con algún valor concreto, como Integer:

Box <Integer> integerBox;
Puede pensar que una invocación de tipo genérico es similar a una invocación de método ordinario, pero en lugar de pasar un argumento a un método, está pasando un argumento de tipo (Integer en este caso) a la clase Box en sí.

Terminología de parámetro de tipo y argumento de tipo: muchos desarrolladores usan los términos "parámetro de tipo" y "argumento de tipo" indistintamente, pero estos términos no son lo mismo. Al codificar, se proporcionan argumentos de tipo para crear un tipo parametrizado. Por lo tanto, la T en Foo <T> es un parámetro de tipo y la Cadena en Foo <String> f es un argumento de tipo. Esta lección observa esta definición al usar estos términos.
Como cualquier otra declaración de variable, este código en realidad no crea un nuevo objeto Box. Simplemente declara que integerBox contendrá una referencia a una "Box de Integer", que es como se lee Box <Integer>.

Una invocación de un tipo genérico se conoce generalmente como tipo parametrizado.

Para crear una instancia de esta clase, use la nueva palabra clave, como de costumbre, pero coloque <Integer> entre el nombre de la clase y el paréntesis:

Box <Integer> integerBox = new Box <Integer> ();

El diamante

En Java SE 7 y versiones posteriores, puede reemplazar los argumentos de tipo necesarios para invocar el constructor de una clase genérica con un conjunto vacío de argumentos de tipo (<>) siempre que el compilador pueda determinar, o inferir, los argumentos de tipo del contexto . Este par de paréntesis angulares, <>, se llama informalmente diamante. Por ejemplo, puede crear una instancia de Box <Integer> con la siguiente declaración:

Box <Integer> integerBox = new Box <> ();
Para obtener más información sobre la notación de diamantes y la inferencia de tipos, consulte Inferencia de tipos.

Parámetros de tipo múltiple

Como se mencionó anteriormente, una clase genérica puede tener múltiples parámetros de tipo. Por ejemplo, la clase genérica OrderedPair, que implementa la interfaz de par genérica:


Las siguientes declaraciones crean dos instancias de la clase OrderedPair:

Pair <String, Integer> p1 = new OrderedPair <String, Integer> ("Par", 8);
Pair <String, String> p2 = new OrderedPair <String, String> ("hola", "mundo");
El código, new OrderedPair <String, Integer>, instancia K como String y V como Integer. Por lo tanto, los tipos de parámetros del constructor de OrderedPair son String e Integer, respectivamente. Debido al autoboxing, es válido pasar un String y un int a la clase.

Como se menciona en The Diamond, debido a que un compilador de Java puede inferir los tipos K y V de la declaración OrderedPair <String, Integer>, estas declaraciones se pueden abreviar usando la notación de diamante:

OrderedPair <String, Integer> p1 = new OrderedPair <> ("Par", 8);
OrderedPair <String, String> p2 = new OrderedPair <> ("hola", "mundo");
Para crear una interfaz genérica, siga las mismas convenciones que para crear una clase genérica.

Tipos parametrizados
También puede sustituir un parámetro de tipo (es decir, K o V) con un tipo parametrizado (es decir, List <String>). Por ejemplo, usando el ejemplo OrderedPair <K, V>:

OrderedPair <String, Box <Integer>> p = new OrderedPair <> ("primes", new Box <Integer> (...));