Mostrando entradas con la etiqueta Vista. Mostrar todas las entradas
Mostrando entradas con la etiqueta Vista. Mostrar todas las entradas

martes, 15 de septiembre de 2009

Tratamiento de imágenes en Java, Parte 1: Introducción y Vista


¡Vamos a ponerle color a Java!


Hola de nuevo blogueros, después de una larga sequía de varios meses ahora traigo el código fuente para la creación de un sencillo editor de imágenes en Java, el cual servirá para dos propósitos, como son el ilustrar el potencial del manejo de archivos e imágenes con Java y conocer algunos componentes adicionales de las interfaces de usuario de Swing.

Sobre la edición de imágenes

Las imágenes digitales consisten en un conjunto de valores numéricos ordenados en forma de arreglos o matrices, cada uno de estos valores representa el contenido del color de un pixel.


Zoom a una imagen digital con el que se aprecian sus pixeles

Los pixeles son los puntos de color que se despliegan en la pantalla al reproducir una imagen en nuestra PC. Al observar estos puntos en conjunto podemos apreciar la imagen en su totalidad, la cual no es otra cosa que una representación digital de una imagen del mundo real, pero que nuestro cerebro interpreta como si se tratara de la imagen original.

Podemos distinguir tres tipos de imágenes por las características de sus pixeles, estás son las las imágenes en escala de grises, las imágenes en blanco y negro y las imágenes a color.


Imagen a escala de grises

A nivel de programación encontramos que lo que las diferencia es la forma en la que varían los valores de los pixeles y cómo son interpretados por nuestra PC.

En las imágenes a color tenemos un conjunto de ternas de valores para representar el color de cada pixel que será visualizado. Así, en una imagen a color un pixel está formado en realidad por tres valores que representan la intensidad de los colores rojo, verde y azul. Al combinar estos tres valores se puede obtener un color diferente a los tres originales, y haciéndolos variar en su intensidad podemos obtener todo un gran conjunto de colores para dibujar. La forma en la que estos colores son combinados obedece al modelo de color RGB, que es el que se utiliza más frecuentemente con las imágenes digitales.


Mezcla activa de colores

En cambio, con las imágenes en escala de grises cada pixel representa un valor equivalente a una graduación de gris. Tal y como dice el artículo enlazado, "las imágenes representadas de este tipo están compuestas de sombras de grises, que van desde el negro más profundo variando gradualmente en intensidad de grises hasta llegar al blanco."

Una de las características interesantes del procesamiento de imagen es que el posible convertir una imagen a color a una imagen equivalente en escala de grises, aplicando un sencillo algoritmo de procesamiento a sus pixeles. Esto se logra calculando el promedio de las ternas de valores de cada uno de los pixeles de la imagen, y reemplazando los tres valores de cada pixel por el promedio obtenido, de forma que los tres colores tengan un mismo valor de intensidad.

Los pixeles de las imágenes en blanco y negro, son similares a las de escala de grises, con la diferencia de que éstos solo pueden contener uno de dos valores de color posibles, el 255 (blanco) y el 0 (negro).

Otra característica importante, es que si modificamos los valores de intensidad de los pixeles de una imagen, podemos hacer que varíen tanto su brillo como su color. Por ejemplo, para aumentar el brillo de una imagen, podemos sumarle un valor escalar a cada una de las ternas de colores de sus pixeles. Mientras que si deseáramos hacer que variara su color, podemos cambiar la intensidad de una de las tres ternas de color de cada uno de sus pixeles, por ejemplo para hacer más azul una imagen podemos sumarle un escalar a todos los valores de azul de sus pixeles.

Teniendo clara esta diferencia entre los tipos de imagen y lo que es un pixel, ahora podemos pasar a la programación de nuestra aplicación, la cual consistirá en una ventana con un menú de opciones, que permitirá cargar archivos de imágenes de nuestros sistema y gracias a una serie de componentes para interfaces gráficas, darles un pequeño tratamiento a estas imágenes. El programa además tendrá la capacidad para salvar las imágenes una vez que hayan sido modificadas por el usuario.


Manos a la obra

Nuevamente nos basaremos en el esquema MVC (Modelo, Vista, Controlador) para la construcción del software, comenzando con la parte de la vista para nuestra ventana y nuestros paneles de opciones.

Componentes de la interfaz de usuario:

JSlider. Utilizaremos este componente para crear una pequeña paleta de intensidad de colores y otra de intensidad del brillo, de forma que podamos modificar con los tonos de rojo, verde y azul de las imágenes, así como el brillo que estas proyectan.
JPanel. Utilizaremos este componente primero, para crear todos los paneles en los que se cargarán los componentes, y además crearemos una clase llamada PanelDeImagen que extenderá a la clase JPanel, para que funcione como un lienzo sobre el cual dibujaremos las imágenes que carguemos y editemos en la aplicación.
JScrollPane. Ya que el tamaño de las imágenes puede varias respecto del tamaño de la ventana de nuestra aplicación, utilizaremos un panel de tipo JScrollPane, el cual tiene la característica de hacer aparecer barras de desplazamiento en sus bordes cuando su contenido excede a su tamaño en pantalla, de forma que podemos cargar imágenes más grandes que nuestra ventana sin problemas.
JMenuBar, JMenu y JMenuItem. Estos componentes nos permitirán crear una barra de menú en el panel superior de nuestra aplicación, el cual contendrá las opciones relacionadas con los archivos, como son abrir un archivo de imagen, guardar la imagen editada, salir de la aplicación. También permitirán activar los paneles que contendrán las opciones para la edición de nuestras imágenes.
JFileChooser. Este componente adicional, nos auxiliará para hacer más sencilla la selección y el guardado de archivos de imágenes, ya que con los objetos de esta clase podemos llamar una ventana de selección de archivos, que nos permitirá elegir los archivos que editaremos en nuestra aplicación de forma visual.

La Vista

El código para implementar la vista de la aplicación aparecerá a continuación, separando cada una de las clases en un apartado.

Código para la implementación de la ventana de la aplicación:

EditorImg.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* Descripción: Esta clase implementa un editor básico de imágenes .jpg y .gif,
* utilizando componentes Swing y una serie de clases para el manejo y procesamiento de imagenes digitales.
* @author Beto González
* @version 1.0
* @category Multimedia
*/
public class EditorImg extends JFrame{
static final long serialVersionUID=10000;
PanelSwing panel;
public static void main(String[] args) {
// TODO Auto-generated method stub
EditorImg editor = new EditorImg();
editor.setBounds(120, 120, 800, 600);
editor.setVisible(true);
editor.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
editor.addWindowListener(new WindowAdapter()
{
public void WindowCloser(WindowEvent e)
{
System.exit(0);
}
});
}

/**
* @Desc Constructor de la clase
*/
EditorImg() {
super("Editor básico de imagenes");
Container contentPane = getContentPane();
panel = new PanelSwing(this);
contentPane.add(panel);
}
}


Código para implementar el lienzo sobre el que se dibujará la imagen:

PanelDeImagen.java
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
/**
* @Desc Clase que extiende a la clase JPanel que se utiliza para crear un panel que permita visualizar imágenes en su interior
* @author Beto González
*
*/
public class PanelDeImagen extends JPanel{
static final long serialVersionUID=10000;
Image img;
Dimension tamaño;
JScrollPane base;
/**
* @Desc Constructor de la clase
*/
PanelDeImagen()
{
setBackground(Color.white);
}
/**
* @Desc Método a través del cual la clase recibe el objeto de la imagen que será visualizada en su interior
* @param i
*/
public void estableceImagen(Image i)
{
img = i;
}
/**
* @Desc Método a través del cual la clase obtiene la referencia hacia el panel en el cual se encuentra contenido.
* @param i
*/
public void estableceBase(JScrollPane contenedor)
{
base = contenedor;
}
/**
* @Desc Método extendido que es llamado cada ves que un objeto de esta clase llama al método repaint(). A este le agregamos
* una funcionalidad adicional que le permite redimencionar el panel que contiene la imagen de acuerdo a las dimensiones de
* ésta
*/
public void paintComponent(Graphics g)
{
super.paintComponent(g);
if (img != null)
{
if(base != null)
{
setSize(new Dimension(base.getWidth()-10,base.getHeight()-10));
setPreferredSize(new Dimension(base.getWidth()-10,base.getHeight()-10));
}
tamaño = new Dimension(getWidth(),getHeight());
int x = tamaño.width - img.getWidth(this);
while (x < 0)
{
tamaño.setSize(tamaño.width+1, tamaño.height);
x = tamaño.width - img.getWidth(this);
}
if(x > 0)
  x = (int) x/2;
int y = tamaño.height - img.getHeight(this);
while (y < 0)
{
tamaño.setSize(tamaño.width, tamaño.height+1);
y = tamaño.height - img.getHeight(this);
}
if(y > 0)
  y = (int) y/2;

if(!getSize().equals(tamaño))
{
setSize(tamaño);
setPreferredSize(tamaño);
}
g.drawImage(img, x, y, this);
}
}
}


La funcionalidad de los objetos de la clase PanelDeImagen consistirá en que recibirán una imagen por medio del método estableceImagen() y la dibujarán en su panel al llamar al método repaint() que heredaron de la clase JPanel. Ya que en esta implementación el panel de las imagenes estará contenida dentro de un objeto JScrollPane, es que además contamos con el método estableceBase(), con el cual podemos guardar una referencia a este panel. Además, debido a que las dimensiones de las imagenes cargada por la aplicación pueden, es que extendimos al método paintComponent(), el cuál además de encargarse de dibujar la imagen dentro del panel, modificará las dimensiones de este y del panel JScrollPane que lo contenga, de forma que se adapten al tamaño de las imágenes y estas puedan ser visualizadas en su totalidad en pantalla.

La siguiente clase implementa la totalidad de los componentes de la interfaz gráfica de la aplicación:

PanelSwing.java
import java.awt.*;
import javax.swing.*;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
/**
* @Desc Clase utilizada para crear los componentes de la interfaz gráfica de la aplicación
* @author Beto González
*
*/
public class PanelSwing extends JPanel {
static final long serialVersionUID = 10000;
String nombreArchivo, ruta;
JMenuBar barraMenu;
JMenu menuArchivo, menuEdicion;
JMenuItem abrir, guardar, salir, brillo, color, escala;
JScrollPane panelDespl;
JPanel panelBajo, panelBrillo, panelColor, panelVacio;
int altura = 80;
Image imagen;
Image imgAux;
EditorImg editor;
PanelDeImagen lienzo;
JSlider jslBrillo, jslRojo, jslVerde, jslAzul;
JLabel lblRojo, lblVerde, lblAzul;
CardLayout esqueInf;
/**
* @Desc Constructor de la clase
* @param editor
*/
PanelSwing(EditorImg editor)
{
this.editor = editor;
this.setLayout(new BorderLayout());
barraMenu = new JMenuBar();
FlowLayout f = new FlowLayout();
f.setAlignment(FlowLayout.LEFT);
barraMenu.setLayout(f);
menuArchivo = new JMenu("Archivo");
menuEdicion = new JMenu("Edición");
abrir = menuArchivo.add("Abrir");
guardar = menuArchivo.add("Guardar");
guardar.setEnabled(false);
menuArchivo.addSeparator();
salir = menuArchivo.add("Salir");
brillo = menuEdicion.add("Ajustar Brillo");
color = menuEdicion.add("Ajustar Colores");
escala = menuEdicion.add("Escala de Grises");
brillo.setEnabled(false);
color.setEnabled(false);
escala.setEnabled(false);
barraMenu.add(menuArchivo);
barraMenu.add(menuEdicion);
this.add("North",barraMenu); //Agregamos la barra de menu
creapanelCentral(); //Creamos el panel en el que se mostrara la imagen seleccionada
creapanelBajo(); //Creamos el panel en el que se mostraran los controles para manipular la imagen
}
/**
* @Desc Método que crea el contenido del panel central de la ventana
*/
private void creapanelCentral()
{
lienzo = new PanelDeImagen();
panelDespl = new JScrollPane(lienzo);
lienzo.estableceBase(panelDespl);
add("Center",panelDespl);
}
/**
* @Desc Método que crea el contenido del panel inferior de la ventana
*/
private void creapanelBajo()
{
panelBajo = new JPanel();
esqueInf = new CardLayout();
panelBajo.setLayout(esqueInf);
panelBajo.setPreferredSize(new Dimension(this.getWidth(),altura));
jslBrillo = new JSlider(SwingConstants.HORIZONTAL,0,100,0);
jslBrillo.setPaintTicks(true);
jslBrillo.setPaintLabels(true);
jslBrillo.setMajorTickSpacing(10);
jslBrillo.setMinorTickSpacing(5);
panelColor = new JPanel();
panelVacio = new JPanel();
panelBrillo = new JPanel(new BorderLayout());
panelBrillo.add("Center", new JLabel("Puedes ajustar el brillo de la imagen",JLabel.CENTER));
panelBrillo.add("South",jslBrillo);
panelBajo.add("carta1", panelVacio);
panelBajo.add("carta2", panelBrillo);
creaPaletas();
esqueInf.show(panelBajo, "carta1");
this.add("South",panelBajo);
}
/**
* @Desc Método que crea el contenido del panel inferior de la ventana
*/
private void creaPaletas()
{
GridBagLayout gridbag = new GridBagLayout();
GridBagConstraints constrain = new GridBagConstraints();
panelColor.setLayout(gridbag);
lblRojo = new JLabel("Rojo");
lblVerde = new JLabel("Verde");
lblAzul = new JLabel("Azul");
constrain.gridx = 0; constrain.gridy = 0;
constrain.gridheight = 1; constrain.gridwidth = 2;
gridbag.setConstraints(lblRojo, constrain);
panelColor.add(lblRojo);
constrain.gridx = 2; constrain.gridy = 0;
gridbag.setConstraints(lblVerde, constrain);
panelColor.add(lblVerde);
constrain.gridx = 4; constrain.gridy = 0;
gridbag.setConstraints(lblAzul, constrain);
panelColor.add(lblAzul);
jslRojo = new JSlider(SwingConstants.HORIZONTAL,0,50,0);
jslVerde = new JSlider(SwingConstants.HORIZONTAL,0,50,0);
jslAzul = new JSlider(SwingConstants.HORIZONTAL,0,50,0);
constrain.gridx = 0; constrain.gridy = 1;
constrain.gridheight = 1; constrain.gridwidth = 2;
gridbag.setConstraints(jslRojo, constrain);
panelColor.add(jslRojo);
constrain.gridx = 2; constrain.gridy = 1;
gridbag.setConstraints(jslVerde, constrain);
panelColor.add(jslVerde);
constrain.gridx = 4; constrain.gridy = 1;
gridbag.setConstraints(jslAzul, constrain);
panelColor.add(jslAzul);
panelBajo.add("carta3", panelColor);
}
}


Hasta este punto hemos completado la parte de la Vista de la aplicación, y nos quedan pendientes el Controlador y el Modelo. Sin embargo, para poder apreciar realmente la funcionalidad de las clases anteriores es necesario probarlas, echando a correr el programa, de forma que podamos visualizarlas y obtener un resultado como el siguiente:



En esta captura, podemos apreciar los tres paneles principales de la aplicación, el menú de opciones en la parte superior, el panel central que contiene los paneles de los componentes de las clases JScrollPane y PanelDeImagen, y finalmente el panel inferior, en el cual se desplegarán los componentes visuales para la edición de las imagenes, una vez que el Controlador y el Modelo estén trabajando.

En el siguiente artículo concluiremos con esta implementación.

Hasta pronto.

viernes, 7 de agosto de 2009

Diseño de interfaces gráficas en Java, parte 2: Swing y los contenedores (Layout)


¿Tocamos Swing?

¡Saludos nuevamente blogueros!

En este post veremos algunas de las características de la biblioteca de clases Swing que posee Java, la cual utilizaremos para el diseño de una interfaz de usuario básica. También revisaremos la gestión de posicionamiento de los componentes gráficos en la pantalla, a través de las diferentes clases de contenedores (Layout) de componentes que posee el lenguaje, en particular le dedicaremos mucha atención a la clase GridBagLayout, que nos permite crear una presentación más elaborada.

También hay que señalar que en esta ocasión nos encargaremos únicamente de la aparición de los elementos gráficos en la pantalla, por lo que trabajaremos con el nivel de Vista, dejando la implementación del Controlador y el Modelo para los siguientes artículos.

La Vista

Bueno, dicho esto entremos en acción. El primer paso consistirá en la creación de una clase que funcionará como un panel que contendrá a los diferentes elementos de la interface que iremos incorporando. Para este propósito es que incluí el siguiente código fuente:

PanelSwing.java
import javax.swing.*;
import java.awt.*;
/**
* @Desc Clase que crea un panel con una serie de componentes visuales que podemos incluir en una ventana de la clase JFrame
* @author Beto González
*/
public class PanelSwing extends JPanel {
    //Atributos de la clase que consisten en una serie de componentes visuales
    public JPanel panelCentral, panelSuperior, panelInferior;
    public JLabel lblTitulo, lblPie, lblMensaje, lblInfo,lblCampo1, lblCampo2;
    public JTextField txtCampo1, txtCampo2;
    public JButton btnAccion1;

    //Constructor de la clase
    PanelSwing() {
        this.setLayout(new BorderLayout());
        this.panelCentral = new JPanel(new GridBagLayout());
        this.panelSuperior = new JPanel(new FlowLayout(FlowLayout.LEFT));
        this.panelInferior = new JPanel(new GridLayout(1,3)); //Este panel consistirá en una tabla de 1 x 3 (1 renglon con 3 columnas)
        this.lblTitulo = new JLabel("Encabezado o Banner de la ventana");
        this.lblPie = new JLabel("Barra de estado");
        this.lblInfo = new JLabel("Autor: Beto González");
        this.lblMensaje = new JLabel("Bienvenido a esta práctica");
        this.lblCampo1 = new JLabel("Nombre completo:");
        this.lblCampo2 = new JLabel("Correo electrónico:");
        this.txtCampo1 = new JTextField("", 30);
        this.txtCampo2 = new JTextField("",20);
        this.btnAccion1 = new JButton("Buscar");
    }
    /**
    * @Desc Método en el que se lleva a cabo la colocación de cada uno de los componentes en el panel principal
    */
    public void estableceContenedores() {
        //Establecemos el panel superior
        panelSuperior.add(this.lblTitulo);
        this.add(panelSuperior, BorderLayout.NORTH);
        //Establecemos el panel central
        GridBagConstraints delimitador = new GridBagConstraints(); //Creamos un delimitador que usaremos para especificar la ubicación de los componentes dentro del panel
        delimitador.ipadx = 8; //Espacio mínimo entre los componentes de un mismo renglon
        delimitador.ipady = 5; //Altura de los componentes
        delimitador.gridx = 0;
        delimitador.gridy = 0;
        delimitador.anchor = GridBagConstraints.EAST;
        panelCentral.add(lblCampo1, delimitador);
        delimitador.gridx = 1;
        delimitador.anchor = GridBagConstraints.WEST;
        panelCentral.add(txtCampo1, delimitador);
        delimitador.gridy = 1;
        delimitador.gridx = 0;
        delimitador.anchor = GridBagConstraints.EAST;
        panelCentral.add(lblCampo2, delimitador);
        delimitador.gridx = 1;
        delimitador.anchor = GridBagConstraints.WEST;
        panelCentral.add(txtCampo2, delimitador);
        delimitador.gridx = 3;
        delimitador.gridy = 3;
        panelCentral.add(btnAccion1, delimitador);
        this.add(panelCentral, BorderLayout.CENTER);
        //Establecemos el panel inferior
        panelInferior.add(lblPie, 0); //Primera celda
        panelInferior.add(lblMensaje,1); //Segunda celda
        panelInferior.add(lblInfo,2); //Tercera celda
        this.add(panelInferior, BorderLayout.SOUTH);
    }
}


La clase recibe el nombre de PanelSwing, ya que básicamente consistirá en un panel que extenderá a la clase JPanel, la cual se encuentra contenida en la biblioteca swing. Este panel representa todo el contenido de la ventana de nuestra aplicación, ya que dentro de él se agregarán otra serie de paneles en los que se colocarán diferentes algunos de los componentes que utilizamos regularmente en el diseño de interfaces de usuario. Estos componentes aparecen declarados como atributos de nuestra clase como podemos ver:

public JPanel panelCentral, panelSuperior, panelInferior; 
public JLabel lblTitulo, lblPie, lblMensaje, lblInfo,lblCampo1, lblCampo2;
public JTextField txtCampo1, txtCampo2;
public JButton btnAccion1;


Entre ellos se incluyen tres paneles, etiquetas, campos de texto y un botón, todos de la biblioteca Swing. Estos objetos son creados dentro del constructor de la clase como podemos ver:

this.setLayout(new BorderLayout());
this.panelCentral = new JPanel(new GridBagLayout());
this.panelSuperior = new JPanel(new FlowLayout(FlowLayout.LEFT));
this.panelInferior = new JPanel(new GridLayout(1,3));
this.lblTitulo = new JLabel("Encabezado o Banner de la ventana");
this.lblPie = new JLabel("Barra de estado");
this.lblInfo = new JLabel("Autor: Beto González");
this.lblMensaje = new JLabel("Bienvenido a esta práctica");
this.lblCampo1 = new JLabel("Nombre completo:");
this.lblCampo2 = new JLabel("Correo electrónico:");
this.txtCampo1 = new JTextField("", 30);
this.txtCampo2 = new JTextField("",20);
this.btnAccion1 = new JButton("Buscar");


Donde además se declara que el Layout de la clase PanelSwing será BorderLayout; este tipo de contenedor nos permite colocar los elementos en cinco ubicaciones distintas dentro de la pantalla, utilizando como referencia los puntos cardinales:



Ya que al momento de agregar cada uno de los elementos a este contenedor se utilizan los valores NORTH, SOUTH, EAST, WEST y CENTER, que corresponden a cada una de las ubicaciones indicadas en la imagen y que se encuentran declarados como valores estáticos dentro de la clase BorderLayout.

El objeto panelSuperior será ubicado en el NORTH de este contenedor, panelCentral en CENTER y panelInferior en el SOUTH. Pueden darse cuenta que este tipo de delimitación está pensada especialmente para diseñar nuestras ventanas para aplicaciones, ya que cada una de las ubicaciones corresponde a un componente específico de la pantalla, por ejemplo la parte Norte puede llevar una barra de menú o de iconos, o una sección de encabezados, los menús también podrían aparecer en las ubicaciones Oeste y Este. Mientras que la parte Central está pensada para colocar el contenido principal de la pantalla, como pueden ser las imagenes, videos, formularios, texto, etc. Finalmente el área Sur por lo general será usada para llevar una barra de estado. Aunque al final la decisión sobre como distribuyamos los elementos de la interface será únicamente de los diseñadores.

El panelSuperior utilizará un Layout de tipo FlowLayout, que ordena los elementos como una secuencia horizontal de izquierda a derecha, similar a como escribimos texto en un editor común. Mientas que panelInferior estará delimitado por un Layout GridLayout, que hace aparecer a los componentes como si estuvieran dentro de una cuadricula de tamaño constante, es decir que en este contenedor se especifica el número de celdas que tendrá, al declarar el número de filas y columnas dentro de su constructor, como vemos que se escribió en el código:

this.panelInferior = new JPanel(new GridLayout(1,3));


Hasta aquí hemos hablado un poco de los tres Layout básicos para interfaces, aún falta ver el más poderoso de todos, es decir el GridBagLayout, que es utilizado por el panelCentral para ubicar los componentes de un formulario. Este contendedor nos permite colocar los componentes dentro de una estructura similar a una tabla HTML, es decir que también cuenta con renglones y columnas, pero con la gran diferencia de que el tamaño y número de las filas y columnas son variables, al mismo tiempo que podemos controlar la ubicación y el relleno que tendrán cada uno de los componentes dentro de la fila. La apariencia que se busca lograr para este ejemplo es la siguiente:


La cual es similar a la apariencia de un formulario básico como los que hemos manejado en las páginas HTML de los temas sobre aplicaciones Web.

Para lograr este propósito es que se incluye el código que vemos dentro del método estableceContenedores():

GridBagConstraints delimitador = new GridBagConstraints();
delimitador.ipadx = 8;
delimitador.ipady = 5;
delimitador.gridx = 0;
delimitador.gridy = 0;
delimitador.anchor = GridBagConstraints.EAST;
panelCentral.add(lblCampo1, delimitador);
delimitador.gridx = 1;
delimitador.anchor = GridBagConstraints.WEST;
panelCentral.add(txtCampo1, delimitador);
delimitador.gridy = 1;
delimitador.gridx = 0;
delimitador.anchor = GridBagConstraints.EAST;
panelCentral.add(lblCampo2, delimitador);
delimitador.gridx = 1;
delimitador.anchor = GridBagConstraints.WEST;
panelCentral.add(txtCampo2, delimitador);
delimitador.gridx = 3;
delimitador.gridy = 3;
panelCentral.add(btnAccion1, delimitador);


Dentro del cual es necesario crear un objeto de la clase GridBagConstraints, que se encarga de especificar la delimitación de cada uno de los componentes que serán colocados en el panelCentral, como vemos en el código, se declara el espaciado entre los elementos de un mismo renglón y la altura de los componentes a través de las propiedades ipadx e ipady respectivamente. Las propiedades gridx y gridy sirven para ubicar al componente en una celda específica. Finalmente el anchor se utiliza para especificar hacia que parte se cargarán los componentes en caso de que esta resulten más pequeños que la celda.

De esta forma después de agregar los componentes a sus paneles correspondientes, solo se agregan estos paneles en las ubicaciones que mencionamos dentro del objeto de la clase PanelSwing.

Para probar este sencillo código, es necesario contar con una clase que extienda a la clase JFrame para crear una ventana de aplicación, y además tener una clase que contenga el método estático main() para su ejecución. En este caso haremos realizaremos ambas tareas en una sola clase con el código que aparece a continuación:

Ventana.java
import java.awt.Container;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.*;
/**
* @Desc Clase que utilizamos para crear una ventana que contendrá el panel principal de la aplicación
* @author Beto González
*/
public class Ventana extends JFrame {
    PanelSwing panelPrincipal;
    /**
    * @param args
    */
    public static void main(String[] args) {
        Ventana ventana = new Ventana();
        ventana.setBounds(120, 120, 800, 600);
        ventana.setVisible(true);
        ventana.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        ventana.addWindowListener(new WindowAdapter()
        {
            public void WindowCloser(WindowEvent e)
            {
                System.exit(0);
            }
        });
    }
    //Constructor de la clase
    Ventana(){
        super("Primer paso en la interfaces gráficas de Java"); //Llamada al constructor de la clase padre
        Container contentPane = getContentPane();
        panelPrincipal = new PanelSwing();
        panelPrincipal.estableceContenedores();
        contentPane.add(panelPrincipal);
    }
}


El código del método estático main lleva a cabo la instanciación de la clase Ventana, especifica sus dimensiones, la hace visible y finalmente habilita la operación de cierre para que el usuario pueda finalizar su ejecución.

Dentro del construcctor de la clase Ventana se instancia a la clase PanelSwing y después de establecer los elementos visuales que componen a este objeto, se agrega al objeto de la clase Container de la ventana para que se incluya en la presentación.

Solo tenemos que agregar este código a un nuevo archivo de clase dentro del proyecto y ejecutarlo. El resultado tiene que ser una ventana con la siguiente apariencia:



Ahora podemos apreciar el trabajo realizado del lado de la presentación de la aplicación, con el plus de que nuestra ventana es 100% redimencionable, ya que todos los componentes ocupan posiciones relativas dentro de ella.

Además de esta propiedad de posicionamiento, Swing nos ofrece la ventaja de poder modificar la apariencia de todos los elementos visuales de una ventana con un par de líneas de código. Por defecto la apariencia que poseen los componentes es Metal, que como podemos apreciar en las imagenes no se parece a las ventanas estandar de ningún sistema operativo, pero además se pueden elegir además las apariencias como Windows (apariencia de ventanas de Windows) y Motif (apariencia de ventanas de Unix); estás tres son lo que se denimona independientes de la plataforma, ya que pueden ser usadas en cualquier S.O. También hay una apariencia llamada Mac, para que nuestras ventanas luscan como las de los sistemas operativos de la Mac, pero a diferencia de las anteriores ésta es dependiente de la plataforma. Pueden encontrar más información sobre la modificación de la apariencia en este artículo de Java en castellano.

Si deseamos modificar la apariencia del ejemplo que acabamos de realizar para que lusca como una ventana de Windows, bastará con agregar unas cuantas líneas de código a la clase Ventana como aparece a continuación:

import java.awt.Container;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.*;
/**
* @Desc Clase que utilizamos para crear una ventana que contendrá el panel principal de la aplicación
* @author Beto González
*/
public class Ventana extends JFrame {
    PanelSwing panelPrincipal;
    /**
    * @param args
    */
    public static void main(String[] args) {
        Ventana ventana = new Ventana();
        ventana.setBounds(120, 120, 800, 600);
        ventana.setVisible(true);
        ventana.setDefaultCloseOperation(DISPOSE_ON_CLOSE);
        ventana.addWindowListener(new WindowAdapter()
        {
            public void WindowCloser(WindowEvent e)
            {
                System.exit(0);
            }
        });
    }
    //Constructor de la clase
    Ventana(){
        super("Primer paso en la interfaces gráficas de Java"); //Llamada al constructor de la clase padre
        Container contentPane = getContentPane();
        panelPrincipal = new PanelSwing();
        panelPrincipal.estableceContenedores();
        contentPane.add(panelPrincipal);
        try{
            UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
        }catch(Exception ex){}
        SwingUtilities.updateComponentTreeUI(getContentPane());

    }
}


Para obener un resultado como el siguiente:



De esta forma es fácil modificar la apariencia general de nuestras aplicaciones de una manera muy fácil.

Y con este último ejemplo es que terminamos este pequeño artículo, espero que se hayan dado una pequeña idea de como pueden integrar la apariencia de una interface de usuario en Java, ya que la parte que sigue es el manejo de eventos del usuario, que corresponde al Controlador de la aplicación. Además de los controles vistos iremos revisando algunos más que nos pueden ser muy útiles, y finalizaremos con un pequeño proyecto que demostrará un poco el poder que tiene la plataforma Java.

Hasta la próxima.