Professional Documents
Culture Documents
Herramientas de
Desarrollo
Interfaces gráficas de usuario: Java Swing
Diego A. Cabra
10
Herramientas de desarrollo
MENÚS
Los menús son un conjunto de botones organizados de forma generalmente
vertical. Estos botones pueden a su vez desplegar otros menús, para dar un
comportamiento de cascada a la navegación por este tipo de elementos.
Generalmente se organizan en un segmento de la parte superior de la ventana
conocido como barra de menú. Esta barra contiene un conjunto de botones
organizados horizontalmente que al presionarlos despliegan sus menús
asociados.
TOOLTIP
Un tooltip es un breve mensaje asociado a un componente, que brinda una
pequeña descripción o ayuda acerca de este. Los tooltips se hacen visibles
cuando el cursor del mouse se posa sobre un componente durante un breve
periodo de tiempo.
ADMINISTRACIÓN DE UNA GUI
Todos los componentes de una interfaz están relacionados de alguna forma con la
aplicación, ya sea directamente, o por medio de su relación con otros
componentes. La funcionalidad de la interfaz depende entonces en gran parte de
cómo se gestionen todas estas relaciones.
DISTRIBUCIÓN
Los componentes deben tener un orden dentro del contenedor en el que se alojan.
Esta distribución puede ser determinada por el desarrollador indicando las
coordenadas de cada componente, o usando estándares de distribución ya
definidos, para solo preocuparse por adicionar el componente al contenedor sin
pensar en las coordenadas exactas de su ubicación.
FOCO
Otro aspecto a tener en cuenta en la administración de una interfaz, es el foco de
los componentes. Cuando un usuario presiona por ejemplo la tecla enter, a que
componente afectará si hay varios en la misma ventana. Esto lo determina el foco
el cual solo le pertenece a un componente y se puede trasladar de uno a otro por
medio del ratón o del teclado con la tecla TAB.
EVENTOS
Luego de diseñada la interfaz, definidos sus componentes, su distribución y
política de transmisión del foco, la interfaz puede lanzarse como un elemento
independiente de la aplicación. El problema claro está, es que no tendría
funcionalidad alguna, y las interacciones del usuario no desencadenarán ninguna
acción. La interfaz gráfica debe conectarse con la aplicación por medio de las
respuestas que debe dar a las interacciones del usuario con los componentes.
La forma como responde cada componente a las interacciones del usuario, está
determinada por un sistema de delegación de responsabilidades en donde el
principal actor es el evento. Cuando un usuario interactúa con un componente, por
ejemplo presiona un botón, se genera un evento que representa esta acción. Este
evento debe ser comunicado a otro actor del sistema llamado controlador. Es este
quien finalmente determina qué hacer cuando reciba un evento. De esta forma, los
componentes actúan como los objetos fuente de los eventos, son estos quienes
liberan los eventos cuando hay interacciones. Los mismos objetos fuente tienen
una lista de los objetos que pueden manejar sus eventos, los controladores,
depende del tipo de evento, se asocia un tipo de controlador. Según el tipo de
evento generado, el componente le informa a su controlador que se debe manejar
un evento. El controlador recibe este evento y efectúa el método correspondiente
para responder el llamado. Más adelante detallaremos este proceso
concentrándonos en el lenguaje Java.
ELEMENTOS DE SWING
Como anteriormente se mencionaba, en Swing cada componente esta modelado
dentro una clase, cada clase define como atributos todas aquellas características
de dicho componente, como color y tamaño. Los componentes están destinados a
hacer parte de un contenedor que también está modelado en clases del API. De
forma general, un contenedor recibe un componente para desplegarlo, haciéndolo
visible al usuario. Java hace uso de la herencia para expresar estas relaciones de
una forma más clara y coherente. Sin el uso de la herencia en este diseño los
contenedores tendrían que tener métodos diferentes para responder mensajes del
tipo: inserte un botón, inserte un campo de texto, inserte un panel, etc.
Todos los contenedores parten de la implementación de AWT para estos
elementos, modelados en la clase Container. La clase Container define los
miembros generales que definen a su vez a las ventanas, por esto existe la clase
Window que hereda de Container. Window define el concepto general de los
contenedores de nivel superior, a partir de esta clase nacen las implementaciones
de Swing para este tipo de elementos. Las ventanas de aplicación están
modeladas por la clase JFrame, los diálogos en la clase JDialog y las ventanas sin
marco por la clase JWindow. Las clases de Swing se diferencian por la J que se
antepone a los nombres. En estos contenedores de nivel superior inicia la
jerarquía de componentes de la interfaz gráfica, pues sobre ellos se insertan los
componentes que se requieran. Un componente se define dentro de Java en su
concepto más general como un objeto de la clase JComponent. Todos los
componentes específicos heredan de esta clase, y de esta forma adquieren la
propiedad de poder ser insertados en un contenedor. En la siguiente figura
podemos ver de forma general esta jerarquía y algunas clases de componentes.
Fig 3. JFrame
Un JFrame administra los componentes que sobre él se van a agregar por medio
de una serie de contenedores auxiliares. El que finalmente va a contener es un
JPanel conocido como Content Pane, el panel de contenido. Estos paneles de
contenido vienen construidos desde la Clase JFrame, y está define métodos para
insertar componentes sobre el panel.
1
Fig 4. Paneles de contenido
LAYOUT MANAGER
La distribución de los componentes sobre un contenedor está determinada por un
conjunto de clases llamadas Layout Managers, de forma simplificada Layout. Los
layout definen un patrón de distribución sobre el contenedor al que se apliquen. Al
definir el layout todos los componentes que se inserten se acomodarán de
acuerdo al patrón de distribución automáticamente. Si se desea, se puede optar
por no utilizar ningún Layout Manager, en este caso la distribución de cada
componente es responsabilidad total del desarrollador, quien tendría que definir en
términos de coordenadas la posición de cada componente que inserte al
contenedor. En java se definen los siguientes Layout Managers:
2
Ibíd.
BoxLayout: esta distribución ubica los componentes en una fila o columna
sencilla, dependiendo de los tamaños de cada componente y su alineación.
3
Fig 6. Distribución BoxLayout
4
Fig 7. Distribución CardLayout
3
Ibíd.
4
Ibíd.
5
Fig 8. Distribución FlowLayout
6
Fig 9.Distribución GridBagLayout
6
Fig 10. GridLayout
5
Ibíd.
6
Ibíd.
JCOMPONENT
Es la superclase de todos los componentes que pueden insertarse en un
contenedor. Define atributos generales como lo son el color, el tamaño o la
posición. Todos los componentes de Swing heredan de esta clase, excepto los
contenedores de primer nivel (JFrame, JDialog y JWindow), que no se pueden
insertar en ningún otro contenedor. Estos componentes se conocen también como
widgets, ya que están listos para usarse dentro de un contenedor, lo único
necesario es instanciar el objeto de la clase deseada e insertarlo al contenedor
que lo hará visible. En el siguiente diagrama se puede apreciar de forma más
detallada las clases que hacen parte Swing, los componentes se pueden apreciar
heredando de JComponent, las clases en color son la base AWT de Swing. Una
descripción detallada de cada clase la podrá encontrar en el Ova de Swing de esta
semana. También encontrará un video diapositiva con un ejemplo práctico de la
construcción de una GUI sencilla.
7
Fig 11. Componentes Swing
7
ALLEN I, Holub. Java Swing Classes [En línea]. http://www.holub.com/goodies/java.swing.html
[Consultado 15/12/2010]
EVENTOS
Las clases involucradas en el manejo de eventos de una interfaz gráfica Java, se
encuentran en el paquete java.awt.event. Allí se pueden encontrar los eventos que
en un momento determinado puede generar un componente. Dependiendo del tipo
de interacción el componente liberará diferentes tipos de eventos.
Existen interfaces para cada tipo de evento, en Java se conocen como Listeners,
oyentes, ya que los objetos de las clases que implementen estas interfaces,
estarán todo el tiempo a la escucha de algún evento para poder manejarlo
adecuadamente. Si se quiere crear un objeto que controle los eventos que se
generan al presionar un botón entonces abría primero que averiguar qué tipo de
evento se libera ante esta acción, en este caso un ActionEvent. Conocido este
aspecto se sabe que la clase del objeto controlador debe tener los métodos que el
botón conoce y utilizará cuando se produzca un AcionEvent, por esto se debe
entonces escribir una clase que implemente la interfaz ActionListener e
implementar los métodos que esta tenga, en este caso solo uno llamado
actionPerformed.
Component listener
Escucha y controla eventos de tipo ComponentEvent, que resultan al cambiar el
tamaño, posición visibilidad de un componente.
Focus listener
Escucha esperando eventos liberados cuando se gana o pierde el foco en un
componente.
Key listener
Controla eventos generados al presionar alguna tecla en el teclado.
Mouse listener
Controla las acciones que se pueden realizar con un mouse sobre un componente,
como movimientos y clics.
Mouse-motion listener
Controla cambios en la posición del cursor del mouse sobre un componente.
Mouse-wheel listener
Controla movimientos de la rueda del mouse sobre un componente.
Otros eventos son específicos para cierto tipo de componentes, por tanto los
controladores que se pueden registrar dependerá del tipo de componente, la
siguiente tabla muestra estas relaciones.
Listener
Componente document,
list
action caret change undoable item window other
selection
edit
button
check box
color chooser
combo box
dialog
editor pane hyperlink
file chooser
formatted text
field
frame
internal frame internal frame
list list data
menu menu
menu key
menu item menu drag mouse
option pane
password field
popup menu popup menu
progress bar
radio button
slider
spinner
tabbed pane
table model
table column
table
model
cell editor
text area
text field
text pane hyperlink
toggle button
tree expansion
tree will expand
tree
tree model
tree selection
Tabla 2. Relaciones entre componentes y sus oyentes78
8
Oracle Corporation. The Java SE Tutorial, Listener Supported by Swing Componentes [En línea].
< http://download.oracle.com/javase/tutorial/uiswing/events/eventsandcomponents.html>.
[Consultado 25/05/2010]
Las interfaces Listener pueden tener declarados muchos métodos. Algunas veces
no basta con saber que ocurrió un evento, sino a partir de que acción ocurrió, por
ejemplo si ocurre un evento del mouse (MouseEvent) para la clase que controle el
evento es importante saber cómo se originó este evento, ¿por medio de un clic
izquierdo?, ¿o acaso por un clic derecho?, ¿o solo porque el cursor del mouse
pasó sobre el componente? Por esto las interfaces para eventos como estos,
definen métodos para controlar cada una de estas posibilidades, y que el
desarrollador sea capaz de controlar una interacción de forma detallada. Como
son interfaces, una clase que las implemente se vería obligada a implementar
todos los métodos de interfaz, o tendría que declararse abstracta lo que no sería
posible para nuestro caso, pues necesitamos objetos de estas clases; dada esta
problemática Java provee en su API un conjunto especial de clases llamadas
adaptadoras, que implementan las interfaces Listener, pero dejan todas las
implementaciones de los métodos vacías. Cada interfaz excepto ActionListener,
tiene su adaptador correspondiente. MouseListener tiene a MouseAdapter,
WindowListener tiene a WindowAdapter. Cuando una clase extiende de una clase
adaptadora, solo necesita sobre escribir el método que necesita, y de esta forma
no contaminar el código fuente con métodos vacios que no hacen nada. Por
ejemplo si lo único que se quiere controlar de un evento de ventana es cuando
esta se va a cerrar, entonces lo más conveniente es escribir una clase
controladora que extienda de WindowAdapter, y que sobre escriba únicamente el
método windowClosing. Si la clase se declara implementando WindowListener, se
tendrá que implementar todos los métodos, así sea dejándolos vacios.
Los look and feel (de ahora en adelante L&F), son componentes que pueden ser
desarrollados o modificados, lo que ha llevado a que estén disponibles desde
diferentes proyectos L&F que se han liberado para la comunidad y por tanto se
pueden usar sin restricciones. Además Java tiene incluido en su API varios L&F
que permiten modificar la apariencia de las aplicaciones adaptándose a los más
comunes sistemas operativos. El entorno de ejecución provee los siguientes L&F:
Para establecer el L&F en una aplicación, basta con incluir una línea en el código
fuente (Mas las que conlleva el adecuado manejo de excepciones), la cual se
recomienda que sea la primera de la aplicación. Esta línea puede usarse para
configurar el L&F de java, del sistema, o especificar la ruta de paquete completa
en donde encontrará el L&F que se desea usar.
Para que la aplicación pregunte por el L&F del sistema sobre el que se va a
ejecutar la aplicación:
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
Crear una clase que extienda del tipo de contenedor de primer nivel que
contendrá la ventana de la aplicación. Por lo general se extiende de JFrame,
indicando que se va a construir un tipo especial de ventana empezando por la
implementación que provee Swing en JFrame.
Declarar como atributos todos los componentes que harán parte de la
ventana, como botones, listas, etiquetas, etc.
Instanciar y configurar correctamente cada componente.
Adicionar según la distribución definida, los componentes al contentPane del
JFrame o a contenedores auxiliares como JPanel. Para distribuir correctamente
los elementos se puede hacer uso de un LayoutManager que permitirá organizar
automáticamente los componentes según el patrón que defina.
Escribir las clases de los objetos que controlarán los eventos, implementando
la interfaz adecuada para el evento que se desea controlar. Estas clases se
pueden escribir como clases internas, independientes, anónimas o incluso la
misma clase de la ventana puede ser controladora a su vez si implementa las
interfaces Listener.
Registrar los controladores en los componentes que pueden llegar a producir
eventos. Este es un punto frecuente de fallos, pues aún si se cumplen todos los
pasos enunciados anteriormente pero no se realiza el registro de los controladores
(que suele ser una única línea de código), la interfaz gráfica no responderá a las
acciones del usuario y será totalmente inútil.