Programación de GUI con Componentens Swing en Netbeans

24 mayo 2009

Programación II Guía de Laboratorio 4: Creación de GUI con componentes Swing en Netbeans
Desarrollo del 25 al 29 de mayo de 2009
Auxiliar de Cátedra: Hugo Alejandro Barrientos


Esta guía está dedicada a crear aplicaciones de escritorio en Java con la ayuda de componentes Swing. El IDE de programación de esta cátedra ha sido Netbeans y este nos proporciona un diseñador para crear aplicaciones de una manera sencilla, sin preocuparnos demasiado del código que implica la forma de la GUI. Simplemente nos debemos de centrar en el funcionamiento de la misma. Sin embargo, el ejercicio está enfocado a realizar algunas partes de la guía de tal manera que debamos conocer el código propio de los componentes Swing, para que nos formemos como buenos programadores de ambientes de escritorio.

En el ejercicio se debe crear un Visor de Imágenes en formatos JPG y PNG. Para ello se crearán: la interfaz en donde el usuario escogerá la imagen que desea visualizar, una clase que administre los archivos seleccionados y una clase que manipule la imagen seleccionada, una clase que herede de un componente swing y agregarla a la paleta de Netbeans para tener un control personalizado y poderlo arrastrar y colocar como cualquier otro del IDE. La estructura de clases será como el siguiente:


Primero crearemos la clase que nos manipule la imagen que seleccionará el usuario. La clase MiImagen es nuestro propio formato personalizado para representar una imagen en la memoria del computador. Se puede pensar que MiImagen es un arreglo bidimensional de pixeles en el cual cada uno de ellos puede tener un color, que se definirá con java.awt.Color. El código sería muy similar a este:

import java.awt.Color;
import java.awt.image.BufferedImage;


public class MiImagen extends java.awt.image.BufferedImage
{
/**
* Crea una instancia de MiImagen a partir de una BufferedImage.
*/
public MiImagen(BufferedImage image){
super(image.getColorModel(), image.copyData(null), image.isAlphaPremultiplied(), null);
}
/**
* Crea una instancia de MiImagen.
*/
public MiImagen(int width, int height){
super(width, height, TYPE_INT_RGB);
}

/**
*Coloca un color en una posición específica
*/
public void setPixel(int x, int y, Color col){
int pixel = col.getRGB();
setRGB(x, y, pixel);
}

/**
* Obtiene el valor de color en una posición específica
*/
public Color getPixel(int x, int y){
int pixel = getRGB(x, y);
return new Color(pixel);
}
}

La clase AdminArchivos ofrece tres métodos: uno para leer desde el disco un archivo de imagen con nombre y devolverlo como una MiImagen, uno para grabar un archivo MiImagen en el disco y otro para abrir una caja de diálogo de selección de archivos que permite que el usuario seleccione la imagen que desea ver. Los formatos permitidos por filtro son JPEG y PNG. Con la creación de esta clase invocaremos código propio de un componente Swing y le daremos forma únicamente con sentencias dejando de lado el diseñador de Netbeans. El código sería muy similar a éste:

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFileChooser;
import javax.swing.filechooser.FileNameExtensionFilter;

public class AdminArchivos {

private static final String IMAGE_FORMAT = "jpg";
private static JFileChooser fileChooser = new JFileChooser();
private static FileNameExtensionFilter filter = new FileNameExtensionFilter("JPG & PNG", "jpg", "png");


public static MiImagen obtenerImagen() throws IOException {
fileChooser.setFileFilter(filter);
fileChooser.setCurrentDirectory(null);
int returnVal = fileChooser.showOpenDialog(null);

if(returnVal != JFileChooser.APPROVE_OPTION) {
return null; // Presiona cancelar
}
File selectedFile = fileChooser.getSelectedFile();

return cargarImagen(selectedFile);
}
public static MiImagen cargarImagen(File imageFile) throws IOException
{
try {
BufferedImage image = ImageIO.read(imageFile);
if(image == null || (image.getWidth(null) <>
return null; //por si ocurre un error al cargar la imagen
}
return new MiImagen(image);
}
catch(IOException exc) {
return null;
}
}

public static void guardarImagen(MiImagen image, File file)
{
try {
ImageIO.write(image, IMAGE_FORMAT, file);
}
catch(IOException exc) {
return;
}
}
}

El elemento Swing que utilizamos para escoger las imágenes es JFileChooser. Creamos un atributo estático para que se puedan seleccionar las imágenes y le asignamos por defecto la carpeta de usuario para cualquier sistema operativo. Ponga mucha atención a la variable estática de tipo FileNameExtensionFilter, ya que es la que nos permite que en el control JFileChooser de Swing solo nos aparezcan imágenes de tipo JPG. Se pueden definir otro tipo de filtros también.

Por último la clase ImagePanel implementa un componente Swing personalizado para mostrar nuestra imagen. Los componentes Swing personalizados pueden crearse fácilmente escribiéndolos como una subclase de algún componente existente, y, como tal, puede insertarse en un contenedor Swing y mostrarse en la GUI como cualquier otro. Nuestro objeto será creado y lo insertaremos en la paleta de Netbeans. Primero debemos crear la clase, con un código como éste:


import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JComponent;

public class ImagePanel extends JComponent
{
private int width, height;

private MiImagen panelImage;

public ImagePanel()
{
width = 360; // Tamaño arbitrario para la instancia vacía de ImagePanel
height = 240;
panelImage = null;
}

public void setImage(MiImagen image)
{
if(image != null) {
width = image.getWidth();
height = image.getHeight();
panelImage = image;
repaint();
}
}

public void clearImage()
{
if(panelImage != null) {
Graphics imageGraphics = panelImage.getGraphics();
imageGraphics.setColor(Color.LIGHT_GRAY);
imageGraphics.fillRect(0, 0, width, height);
repaint();
}
}

// Los siguientes métodos son redefiniciones
// de métodos heredados de las superclases.

@Override
public Dimension getPreferredSize()
{
return new Dimension(width, height);
}

@Override
public void paintComponent(Graphics g)
{
Dimension size = getSize();
g.clearRect(0, 0, size.width, size.height);
if(panelImage != null) {
g.drawImage(panelImage, 0, 0, null);
}
}
}

Luego de escrita la clase, construimos el proyecto para que nos genere una JAR. Así podremos añadir nuestro componente personalizado a la paleta de Netbeans. Nos vamos al menú Tools y en la opción Palette damos click a Add Swing/AWT Components.


Seleccionamos la opción Add from JAR. Buscamos el JAR generado de nuestro proyecto y lo seleccionamos. Luego volvemos a seleccionar entre una lista de las clases de dicho JAR y damos click a la clase del componente, en este caso es ImagePanel. Seleccionamos la categoría de Swing Components y damos por finalizada la inserción a la paleta.


Ahora crearemos un nuevo JFrame Form con ayuda del diseñador de Netbeans. En el paquete de source code lo creamos y colocaremos los siguientes elementos:

• 1 JMenuBar
o 3 JMenuItem. Las opciones serán: Abrir, Limpiar y Salir
o A cada JMenuItem se le deberá asignar un evento ActionPerformed
• El Layout será BorderLayout
• 2 JLabels. Uno tendrá colocación PAGE_START y el otro PAGE_END
• 1 ImagePanel, con colocación de Layout CENTER. Nombre de instancia: “panelI”.
• En el constructor, asignamos el título de la ventana como “Visor de Imágenes”
• También en el constructor, Colocamos la posición relativa en “null”
• Si se quiere la ventana maximizada desde el principio, se hace con la siguiente sentencia:
this.setExtendedState(javax.swing.JFrame.MAXIMIZED_BOTH);


La ventana en modo diseño debe lucir como la siguiente:

Ahora para que nos muestre una imagen, debemos agregar las sentencias necesarias en el método que desencadena la acción de ActionPerformed en el JMenuItem para abrir imagen. Las sentencias podrían ser similares a éstas:

try {
MiImagen img = AdminArchivos.obtenerImagen();
panelI.setImage(img);

this.lblNombreImg.setText(img.getClass().getName());
this.lblEstado.setText("Imagen cargada con éxito");
this.setLocationRelativeTo(null);
this.pack();

panelI.repaint();

} catch (IOException ex) {
Logger.getLogger(visor.class.getName()).log(Level.SEVERE, null, ex);
}

La aplicación debe lucir así: