De la misma forma que podemos diseñar una interfaz gráfica en una sola clase, es recomendable dividir las diferentes secciones (componentes) de una GUI en diferentes clases. De este modo será más fácil reutilizar algunos componentes en interfaces gráficas de otras aplicaciones, además de tener clases más pequeñas y, por lo tanto, más escalables o más fáciles de depurar.
En el siguiente bloque se muestra el uso de ciertos componentes ya existentes en la API de Java, y también se mostrará cómo crear nuestros propios componentes, e incorporarlos a nuestra interfaz.
Un tipo de componentes muy útil en nuestra interfaz gráfica son los cuadros de dialogo. Un cuadro de diálogo es toda aquella ventana que me muestra una información y tiene botones a través de los cuales se da una respuesta pulsando un botón (Aceptar, Cancelar, Omitir, Guardar, Cargar, etc).
La clase JFileChooser permite crear cuadros de diálogo para seleccionar ficheros, por ejemplo para las operaciones de guardar o abrir ficheros. Para crear una instancia usaremos:
JFileChooser selector = new JFileChooser(); int opcion = selector.showOpenDialog(null); if(opcion == JFileChooser.APPROVE_OPTION){ //Ha pulsado “Aceptar” File fichero = selector.getSelectedFile() // Procesamos fichero }
Métodos principales:
showOpenDialog(null)
: Abre un cuadro de dialogo de apertura de fichero.showSaveDialog(null)
: Abre un cuadro de dialogo de guardado de fichero.setCurrentDirectory(File ruta)
: Establece la ruta en la que se abre el cuadro de dialogo.getSelectedFile()
: Devuelve un objeto File con el fichero seleccionado.Referencias: https://docs.oracle.com/javase/tutorial/uiswing/components/filechooser.html
La clase JOptionPane posee una serie de métodos estáticos mediante los cuales puede mostrar pequeños cuadros de dialogo genéricos. Me pueden servir para mostrar un mensaje, pedir un dato, pedir que pulse un botón, etc. Los métodos más útiles se detallan a continuación:
El método showMessageDialog()
recibe como mínimo 2 parámetros, el objeto del que depende y el mensaje.
El primer parámetro puede ser null y siempre se mostrará sobre la ventana que lo lanza. Si se indican más parámetros se puede modificar el título de la ventana y el icono que muestra.
JOptionPane.showMessageDialog(null, "Tu programa te saluda");
También podemos indicarle distinta finalidad al cuadro de dialogo:
//Icono Warning JOptionPane.showMessageDialog(null,"Mensaje", "Titulo", JOptionPane.WARNING_MESSAGE); //Icono Error JOptionPane.showMessageDialog(null,"Mensaje", "Titulo", JOptionPane.ERROR_MESSAGE); //Icono Personalizado JOptionPane.showMessageDialog(null,"Mensaje", "Titulo", JOptionPane.INFORMATION_MESSAGE, icono);
Si lo que queremos es pedir datos al usuario mediante una ventana de dialogo, usaremos el método showInputDialog()
.
El método recibe el objeto padre (puede ser null) y un String con el mensaje que queremos mostrar. El texto que introduce el usuario es devuelto como tipo String por este método:
String nombre = JOptionPane.showInputDialog(null, "Introduce tu nombre");
Si lo que queremos es pedir la confirmación del usuario para realizar algo, podemos usar el método
showConfirmDialog()
.
Este método devuelve un int: 0,1 ó 2, dependiendo de la respuesta del usuario. (0 = Si; 1 = No; 2 = Cancelar) Recibe como parámetro la referencia a la ventana padre (podemos indicar null) y el String con el mensaje a mostrar:
int respuesta = JOptionPane.showConfirmDialog(null,"¿Desea eliminar este elemento?");
Con el método showOptionDialog()
podemos ofrecer una serie de botones para seleccionar una opción en concreto.
Es el método que más parámetros recibe, pero su uso es igual de sencillo. Debemos indicarle un array con las opciones que queremos que ofrezca.
String[] opciones = {"Opcion A", "Opcion B", "Opcion C", "Opcion D"}; int respuesta = JOptionPane.showOptionDialog(frame, "Es necesario que seleccione una opcion", "Titulo", JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE, null, opciones, null);
Además del cuadro de dialogo de tipo JFileChooser
para seleccionar ficheros, Java ofrece otra clase con un funcionamiento similar: la clase JColorChooser
. En este caso este cuadro de dialogo devuelve un objeto de tipo Color
que representa el color elegido.
Para mostrarlo usamos el siguiente método estático que recibe 3 parámetros:
JColorChooser.showDialog(componente, titulo, colorInicial)
JFrame
, un JPanel
. Si indicamos null
se muestra encima de la ventana anterior.String
con el título del cuadro de dialogo que se despliega.null
será el color blanco.JLabel label = new JLabel("Texto en color"); //Selecciono un color del cuadro de dialogo Color colorSeleccionado = JColorChooser.showDialog(contentPane, "Elige un color", Color.BLACK); //Cambio el color del texto de la etiqueta label.setForeground(colorSeleccionado);
Representan a las ventanas secundarias que tendrá nuestra aplicación. No son elementos que contiene la paleta, sino que los crearemos en una clase nueva como contenedor de alto nivel de tipo JDialog. Posteriormente serán llamados desde nuestra aplicación ante un evento (p.e. pulsar un botón). Se crean de la misma forma que una ventana basada en un JFrame, y su clase será instanciada desde algún método de la ventana principal.
Los elementos JFileChooser o JOptionPane son instancias de JDialog.
La peculiaridad que tienen es que permiten ser definidas como ventanas modales. Esto quiere decir que permiten bloquear el uso y acceso al resto de la aplicación mientras están abiertos.
setModal(true);
Para crear un cuadro de diálogo accedo al menú de WindowBuilder y selecciono crear un elemento JDialog. Automáticamente se me genera una clase con el código referente a la creación de dicho diálogo. Se crea una cuadro genérico con dos botones (Aceptar, Cancelar).
Debemos añadir los listeners a dichos botones.
Puedo añadir todos los componentes que quiera del mismo modo que a un JFrame (Ventana principal del programa).
Posteriormente puedo crear mi cuadro de diálogo (JDialog), llamando a su constructor:
MiDialogo cuadro = MiDialogo();
Si necesito que mi cuadro reciba algún tipo de datos de la ventana principal, puedo pasarlo como atributo al constructor:
MiDialogo cuadro = MiDialogo(listaAlumnos);
En el siguiente video se expone la creación de un JDialog personalizado para seleccionar el idioma de mi aplicación. Aunque trata también algún aspecto de la internacionalización de una aplicación (Bloque 3), la creación del cuadro de diálogo sirve perfectamente de guía.
Del mismo modo que podemos utilizar elementos que heredan de JDialog, podemos crear elementos que heredan de cualquier clase de Java: JButtons, JTextFields o incluso crear JPanel con varios componentes en su interior.
Estas clases las podemos escribir a mano o crear con algún diseñador de interfaces. WindowBuilder no solo nos permite crear clases con contenedores de alto nivel (JFrame o JDialog), sino que también nos permite crear clases de tipo JPanel. Los paneles son un tipo de componente muy habitual para personalizar.
En el siguiente video se detalla la creación de un panel personalizado, el empaquetado y el uso en otro proyecto:
Las clases Java de nuestros componentes pueden ser guardadas en un archivo JAR. Al igual que utilizamos librerias de clases añadiendo fichero JAR a nuestro proyecto, nosotros también podemos hacerlo del mismo modo.
Para guardar en un fichero jar las clases que necesitamos usar en otros proyectos, debemos seleccionarlas para empaquetarlas. En Eclipse desde la pestaña File, seleccionaremos la opción Export.
Una vez en esta nueva ventana abriremos la sección Java, y pulsarmos sobre JAR File.
Después de pulsar sobre JAR file, se abrirá otra ventana en la que podemos seleccionar las clases que queremos guardar en nuestro JAR. Podemos seleccionar clases de distintos proyectos que queramos empaquetar.
Finalmente seleccionaremos un destino para almacenar nuestro archivo JAR. Siempre es recomendable guardar todas las librerías juntas en algún lugar de mi equipo.
Los ficheros Jar en IntelliJ se denominan artifacts.
El atajo más directo para crear una librería Jar con solamente algunas clases es la siguiente: Desde la sección Project Structure
dentro del menu File
, nos situamos sobre Artifacts
.
Ahí pulsamos sobre el boton (+) para crear un nuevo artifact Jar vacio.
En el siguiente cuadro de dialogo, podemos indicar un nombre para el fichero , indicar si queremos que se cree cuando hagamos un Build del proyecto, y en la sección inferior, si queremos que nuestras librería de clases esté organizada en directorios (del mismo modo que los packages), podemos pulsar sobre el botón crear directorio:
y después pulsando sobre el símbolo (+) debemos seleccionar los archivos .class que queremos que incluya:
Ahí en el selector de ficheros, buscamos los archivo .class que queramos incluir en nuestro Jar. Una vez indicamos pulsamos sobre aceptar finalizando la creación del artifact.
Para generarlo el fichero Jar iremos al menú Build de IntelliJ, y pulsaremos sobre build project
en caso de que a la hora de configurar el artifact marcaramos el checkbox que indicamos en esta sección. En caso contratio Pulsamos sobre build artifacts
.
Para usar componentes en un mismo proyecto, tan solo debemos importar la clase desde el paquete en el que esté. Si queremos añadirla a WindowBuilder, desde la opción choose component se añade la clase a la paleta de componentes, como se indica en el siguiente punto.
Para incluir ficheros JAR con clases y poderlas usar en otros proyectos debemos incluirlas en el CLASSPATH del proyecto. Es recomendable antes de ellos almacenarlas en un directorio dentro de nuestro proyecto.
Ahora podemos usar las clases que tenemos en el paquete JAR.
En caso de que necesitemos añadir un componente gráfico de alguna de nuestras librerías, a la paleta de componentes de WindowBuilder, tan solo tenemos que pulsar en Choose components y escribir el nombre de la clase.
Desde la sección de Project Structure dentro del menú File, podemos añadir librerías a nuestro proyecto:
Dentro de la sección libraries pulsamos sobre el simbolo (+) y añadimos la ruta del directorio que contiene las librerias en formato Jar dentro de nuestro proyecto.
Una vez que hemos añadido las librerías con nuestras clases, desde la paleta de componentes del diseñadir UI Designer, iremos a la sección Palette y haremos click derecho sobre Non-Palette Component y crearemos un nuevo grupo para nuestros componentes:
Cuando tenemos un nuevo grupo para nuestros componentes, vamos a añadir los componentes que queramos a la paleta. Empezamos por hacer click derecho sobre nuestro nuevo grupo de componentes, e indicaremos la opción Add Component to Palette….
Se abrirá un cuadro de dialogo en el cual pulsaremos sobre el botón (…) a la derecha del campo Class:
Buscaremos la clase que necesitemos y nos aseguramos que está en el paquete correcto (Puede que exista clases con el mismo nombre). Seleccionamos y pulsamos aceptar en los dos cuadros de dialogos. Con ello hemos conseguido añadir un nuevo componente a nuestra paleta del diseñador.
Se llama GUI dinámica a una interfaz que crea o elimina componentes en tiempo de ejecución. Se suelen utilizar para representar listas de elementos, y aunque se pueden realizar mediante algún atajo (modificando la visibilidad de componentes o paneles en tiempo de ejecución), normalmente se utiliza algún tipo de componente personalizado que se añade dinámicamente a una sección de la GUI. Cada elemento suele ser un panel que contiene otra serie de componentes.
En la siguiente imagen vemos un programa de descargas, cuyo panel central va añadiendo nuevos elementos (subpaneles de descarga), conforme se van poniendo en cola más descargas:
En el video que se incluye a continuación se muestra un ejemplo de creación de una interfaz dinámica:
En el siguiente video, además de usar una interfaz dinámica gestionamos los eventos de nuestro componente personalizado mediante el Event Dispatcher:
En los siguientes dos videos utilizamos el IDE IntelliJ:
Creación de GUI dinámica (IntelliJ)
GUI dinámica: Manejar componentes (IntelliJ)
Otra forma de usar componentes personalizados para listar elementos es hacer que un elemento JList
o JComboBox
muestre nuestros componentes en sus celdas. Es decir, diseñar el aspecto de las celdas de JList, por ejemplo. Para ello nuestro componente debe implementar la interface ListCellRenderer
.
Podemos hacer, por ejemplo, que cada celda de un JList
o JComboBox
tenga además del texto, una pequeña imagen. El elemento gráfico que permite tener una imagen y un texto es el JLabel. Por lo tanto tenemos que crear nuestro componente personalizado a partir de la clase JLabel
y que dicha clase implemente ListCellRenderer
. La idea es la misma si queremos usar un JPanel
personalizado.
En el método getListCellRendererComponent()
que nos implemente la interface, aunque parezca complejo, tan solo tenemos que dar valor a las dos propiedades de la JLabel que nos interesan: el texto y la imagen.
import java.awt.Component; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.ListCellRenderer; import base.Pais; public class PaisRenderer extends JLabel implements ListCellRenderer<Pais> { public CountryRenderer() { //Indico que la JLabel no es transparente setOpaque(true); } //Método implementado por la interface ListCellRenderer @Override public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) { //Lo único que hay que hacer es indicar los valores que tendrá JLabel Pais pais = (Pais) value; //Si renderizo en un comboBox, compruebo si pais es null String nombreImagen = pais.getNombreImagen(); ImageIcon imageIcon = new ImageIcon(getClass().getResource("/images/" + nombreImagen + ".png")); //Añado la imagen correspondiente a la JLabel setIcon(imageIcon); setText(pais.getNombre()); //Indico los colores de fondo de los elementos seleccionados y no seleccionados if (isSelected) { //Usa por defecto los que use el JList setBackground(list.getSelectionBackground()); setForeground(list.getSelectionForeground()); } else { setBackground(list.getBackground()); setForeground(list.getForeground()); } //Devuelvo el objeto PaisRenderer return this; } }
En el ejemplo anterior se usa un componente personalizado (JLabel) para representar países en una lista. La clase Pais
solo tiene dos atributos: nombre y nombreImagen.
En caso de que el renderer se use para un JComboBox
debo añadir en el método getListCellRendererComponent()
un control sobre valores null
antes de realizar las demás instrucciones:
//Compruebo si el valor es null if(value == null){ setIcon(null); setText(""); } else { Pais pais = (Pais) value; String nombreImagen = pais.getNombreImagen(); . . . }
Para usar el JList
o un JComboBox
con el Renderer creado:
JList<Pais> lista = new JList<Pais>(); DefaultListModel<Pais> dlm = new DefaultListModel<Pais>(); //Asocio el renderer lista.setCellRenderer(new PaisRenderer()); //Ahora usamos el modelo de forma habitual: dlm.addElement(unPais);
Las celdas de JList
o JComboBox
solo sirven para mostrar información; No son elementos interactivos. Si nuestros componentes tienen funcionalidad extra (p.e. botones), estos botones aparecerán, pero no se podrán usar.
© 2024 Fernando Valdeón