Parece ser que están dando guerra las tablas xD A continuación les dejo un post con un ejemplo de tabla con cajas seleccionables y otros valores que son en éste caso enteros. Luego tenemos un botón con el que podemos recorrer la tabla (el modelo que representa los valores de la tabla) e ir borrando las filas que tienen la caja seleccionable seleccionada, valga la redundancia…
private static final long serialVersionUID = 1L; private JPanel contentPane; private JTable table;
/** * Launch the application. */ public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { try { TablaSeleccionable frame = new TablaSeleccionable(); frame.setVisible(true); } catch (Exception e) { e.printStackTrace(); } } }); }
/** * Create the frame. */ public TablaSeleccionable() { setTitle("Tabla seleccionable by jnj"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds(100, 100, 604, 347); contentPane = new JPanel(); contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); setContentPane(contentPane); contentPane.setLayout(null);
JScrollPane scrollPane = new JScrollPane(); scrollPane.setBounds(10, 11, 560, 227); getContentPane().add(scrollPane);
String[] columnas = { "Los checkbox", "Un dato", "Otro dato" }; final DefaultTableModel modelo; modelo = new DefaultTableModel(null, columnas);
// hay que programar nuestra propia función getColumnClass para // poder poner controles como por ejemplo el checkbox con los datos // booleanos table = new JTable(modelo) { private static final long serialVersionUID = 1L;
@Override public Class getColumnClass(int column) { switch (column) { case 0: return Boolean.class; case 1: return Integer.class; case 2: return Integer.class; default: return Boolean.class; } } };
// INSERTA UNOS DATOS EN LA TABLA for (int i = 0; i < 10; i++) { Object[] nuevafila = { false, i, i }; modelo.addRow(nuevafila); }
// BOTON QUE ELIMINA FILAS, // Recorre de atrás hacia adelante las filas eliminando las // seleccionadas. JButton btnEliminarFilas = new JButton("Eliminar filas"); btnEliminarFilas.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { for (int i = 9; i >= 0; i--) { // si la fila i, columna 0 es true, es porque está // seleccionado el checkbox, entonces borra if (((Boolean) modelo.getValueAt(i, 0)) == true) { modelo.removeRow(i); } }
En el ejemplo he puesto un DefaultTableModel por su simplicidad. Hay otras formas de hacer lo mismo, como viene siendo habitual en programación. Espero que sirva.
Siguiendo los minitutoriales de la serie Swing, he desarrollado éste post sobre tablas para aprender a usar un poco más las tablas. Vamos al grano. Introducción
Es necesario:
Entorno de Desarrollo.
El JDK o JRE, yo he usado el JDK 1.7, pero ya Swing no sigue en desarrollo así que no tiene mucha importancia la versión que uses.
Trato de explicar cómo poner cajas seleccionables (checkbox), listas desplegables (combobox) y cajas de texto formateado, en una tabla. En el ejemplo he puesto una caja que sólo admite un número de 3 dígitos. Más adelante lo vemos.
Para manejar los datos puedes usar directamente el modelo de datos que autogenera un JTable, puedes usar un AbstractTableModel como aparece en muchos de los tutoriales que he encontrado por Internet, o puedes usar una implementación de DefaultTableModel, que internamente usa un vector con el que te permite añadir o borrar filas de forma dinámica. Yo he usado un DefaultTableModel que me ha parecido más interesante porque muchas veces necesitamos añadir o borrar datos de una tabla.
getContentPane().setLayout(null); JScrollPane scrollPane = new JScrollPane(); scrollPane.setBounds(10, 11, 810, 271);
getContentPane().add(scrollPane);
// usa mi modelo de datos modelo = new MiModelo(); // crea la tabla con mi modelo tabla = new JTable(modelo);
// scrollPane.setViewportView(tabla);
// EDITORES PERSONALIZADOS EN LA TABLA // la caja seleccionable, el checkbox lo pone sólo // con la función getColumnClass TableCellEditor editor;
// configura el combo JComboBox comboBox = new JComboBox(); comboBox.addItem("Opción A"); comboBox.addItem("Opción B"); comboBox.addItem("Opción C");
// configura el campo de texto JFormattedTextField campoTexto = null; try { // sólo admite 3 números campoTexto = new JFormattedTextField(new MaskFormatter("###")); } catch (Exception e) { System.out.println(e.getMessage()); }
// combo a la columna 3 editor = new DefaultCellEditor(comboBox); tabla.getColumnModel().getColumn(3).setCellEditor(editor);
editor = new DefaultCellEditor(campoTexto); tabla.getColumnModel().getColumn(4).setCellEditor(editor);
// capturador de eventos del ratón en la tabla // autogenerado con Eclipse tabla.addMouseListener(new MouseListener() {
@Override public void mouseReleased(MouseEvent e) { // TODO Auto-generated method stub
}
@Override public void mousePressed(MouseEvent e) { // TODO Auto-generated method stub
}
@Override public void mouseExited(MouseEvent e) { // TODO Auto-generated method stub
}
@Override public void mouseEntered(MouseEvent e) { // TODO Auto-generated method stub
}
@Override public void mouseClicked(MouseEvent e) { // TODO Auto-generated method stub
int fila = tabla.rowAtPoint(e.getPoint()); int columna = tabla.columnAtPoint(e.getPoint());
JOptionPane.showMessageDialog(null, "Valor de la celda: " + tabla.getValueAt(fila, columna).toString()); } });
}
// modelo de datos, usa DefaultTableModel que internamente // tiene un vector que nos permite por ejemplo añadir una fila // fácilmente con addRow... private static class MiModelo extends DefaultTableModel {
// constructor de la clase, simplemente pone unos datos en el vector de // datos. public MiModelo() { super(nombresColumna, 0);
// carga unos datos iniciales... for (int i = 0; i < 10; i++) {
Object[] aux = { i, Math.random(), i % 2 == 0, "Opción A", "123" };
addRow(aux);
} }
@Override public Class getColumnClass(int columnIndex) { // necesario para que dibuje la tabla con los tipos de columna return tiposColumna[columnIndex]; }
}
}
También he añadido la captura de eventos del ratón. Al escribir tabla.addMouseListener(new Mouse… podemos usar la combinación de teclas Ctrl + espacio y Eclipse nos intentará autogenerar el código. Ésto mismo se puede usar en cualquier momento, si Eclipse detecta que estás escribiendo algo que conoce te lo pone en un listado o si sabe seguro lo que vas a escribir lo autogenera. Es un comportamiento muy útil.
Explicaciones
He usado un DefaultTableModel que internamente tiene un vector de filas, para poder usar la función addRow() y así simplificar todo un poco que para eso está la clase DefaultTableModel.
La historia de configurar la tabla está en la clase MiModelo, espero haber dejado lo bastante comentado el código como para no explicar de nuevo aquí lo que hace. De todas formas si queda duda, mensaje aquí abajo que lo contesto en cuanto pueda.
Podemos obtener el vector de datos de la forma:
Vector data = modelo.getDataVector();
Hay muchas funciones interesantes que nos permitirán hacer lo que necesitemos. Para hacerse una idea, en mi Eclipse pongo modelo. y se me despliega el listado de funciones que tengo disponibles:
Tenemos la que ya he dicho addRow para añadir una fila, getRowCount que devuelve el número de filas, y muchas más. Algunas funciones necesitan un post entero para explicarlas así que lo dejaré aquí.
Donde pone tabla.addMouseListener(… está todo lo relacionado con el ratón. Podemos programar acciones para cuando se hace click, cuando se aprieta el botón, cuando se libera, etcétera…
Sobre los editores personalizados en una tabla hay que decir que admite cajas chequeables, listas desplegables y campos de texto. Además se pueden crear nuevos editores, como por ejemplo el que cita en la documentación oficial de un editor de celda que nos permite elegir entre una tabla de colores.
Más información
Me remito como siempre a la documentación oficial:
De todas formas hay muy buena información en otras páginas web, espero que si has leido éste tutorial por lo menos te haya aclarado algo, o te sirva el código fuente.
Siguiendo con los minitutoriales de Swing, ha llegado la madre del cordero del manejo de datos. Tarde o temprano necesitamos visualizar, modificar, borrar o guardar datos. Podemos hacer formularios, recorrer tablas guardadas en variables ocultas, usar listas, al final la manera más simple de mostrar muchos de éstos datos es una tabla, que en Swing se llama JTable.
Para seguir éste tutorial hace falta, un entorno de desarrollo (por ejemplo Eclipse), el JDK o el JRE, y un poco de paciencia xD
Si eres un máquina entonces descárgate directamente el código y no te olvides de visitarme de vez en cuando, a ver si aumentan las visitas =)
A saber
Antes de empezar hay que saber que un JTable es un componente Swing, que va a visualizar unos datos que se almacenan internamente en una variable de tipo TableModel. No se recomienda usar directamente pero si no lo necesitamos para qué vamos a complicarnos más la vida en usar otros métodos. Lo he usado para no complicar el ejemplo.
En el ejemplo se va a usar un DefaultTableModel el cual va a guardar referencia del TableModel que tiene la tabla. Entonces cuando se modifica, en éste caso añadiendo más filas, automáticamente la tabla se redibuja y muestra los datos actualizados. Se recomienda usar un AbstractTableModel para manejar los datos pero eso es otro tema.
Comenzando
Gran parte del código se puede generar con Eclipse. Así que los pasos a seguir para emepezar son:
Crear un nuevo proyecto Java.
Añadir un JFrame que genere todo el esqueleto de la ventana.
En el Swing Designer en vista de diseño poner un Absolute Layer que nos permitirá posicionar los elementos donde queramos.
Poner un JScrollPane y dentro de él el JTable, de esta manera si la tabla crece más que lo que vemos apareceran barras de scroll como las de la imagen anterior.
El botón de abajo que meterá contenido en la tabla.
Hecho ésto hago doble click en el botón y me muestra todo el código recién generado listo para seguir implementando.
El código
A continuación el código lo más legible que he podido poner:
// versión private static final long serialVersionUID = 1L;
// la tabla private JTable table;
// el modelo de tabla, aquí van a estar los datos. private DefaultTableModel model;
// función principal public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { try { JavaSwing9JTable frame = new JavaSwing9JTable(); frame.setVisible(true); } catch (Exception e) { e.printStackTrace(); } } }); }
// constructor del frame que contruye toda la ventana... public JavaSwing9JTable() { //título setTitle("Java Swing 9 El JTable"); // cuando cerramos la ventana se cierra la aplicación por completo setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // dimensiones y posición setBounds(100, 100, 596, 331); // establece una capa absoluta para posicionar los elementos donde queramos getContentPane().setLayout(null);
// el panel con barras de scroll automáticas JScrollPane scrollPane = new JScrollPane(); // dimensiones y posición del panel de scroll scrollPane.setBounds(10, 11, 560, 227);
// se añade el panel de scroll a la ventana getContentPane().add(scrollPane);
// nombre de las columnas String[] columnNames = { "ID", "Un dato", "Otro dato" };
// creo un modelo de datos, sin datos por eso 'null' y establezco los // nombres de columna model = new DefaultTableModel(null, columnNames); // creo la tabla con el modelo de datos creado table = new JTable(model);
// se pone la tabla en el scroll scrollPane.setViewportView(table);
// código del botón JButton btnAadirLnea = new JButton("Meter contenido"); btnAadirLnea.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) {
// aquí se añaden datos a la tabla for (int i = 0; i < 100; i++) {
// creo un vector con una fila Object[] aux = { i, i * 34, Math.random() };
// añado la fila al modelo model.addRow(aux);
}
} }); // dimensiones y posición del botón btnAadirLnea.setBounds(10, 249, 267, 23); // pongo el botón en la ventana getContentPane().add(btnAadirLnea);
Ésta es la forma más simple que he encontrado de usar una tabla de Swing. El paso siguiente es usar un AbstractTableModel, crearnos una clase que la implemente, con la que podemos entonces hacer cosas como meter cajas seleccionables o cuadros desplegables en las filas.
Más información
Hay mucha información en Internet, me remito primero a la documentación oficial, está en inglés y es muy extensa:
Espero que sirva. En próximos posts espero ver cómo controlar los clicks del ratón, esos temas que citaba de la clase AbstractTableModel, formularios maestro-detalle, etcétera…
Volviendo de nuevo con un nuevo post sobre Swing, esta vez os dejo un componente un poco más complicado de entender, el JTree. Su uso con el Swing Designer es bien simple de ponerlo en un frame y dibujar su interfaz, pero cuando entremos en su estructura que tenemos para manejarlo necesitaremos tener conocimientos de lo que es un Tipo Abstracto de Datos (TAD para los amigos), y en concreto lo que es un árbol. Lo que se puede hacer y cómo se estructura internamente lo supongo por entendido. Si no es el caso mejor empezar por ver ésto, sino lo de a continuación puede convertirse en un jeroglífico.
Para éste tutorial he utilizado Eclipse Juno y la versión 7 update 9 del JDK. Lo primero es crear, dentro de Eclipse, en la ventana del explorador de proyectos, un nuevo proyecto de Java. Luego crear un nuevo JFrame con el asistente de Eclipse y ya tenemos el esqueleto del programa listo para empezar. Yendo al diseñador de formularios del Swing Designer le añades un Absolute layout al formulario para poder posicionar los componentes donde quieras del formulario, y entonces para éste ejemplo le he añadido dos botón y un componente JTree. El componente JTree está a su vez dentro de un JScrollPane para que se muestren las barras de scroll si el contenido es más grande que lo que se puede ver.
Antes de continuar te debe de haber quedado una ventana parecida a la de la imagen de inicio.
En la versión que tengo, cuando añado el JTree al Frame, se crea con un contenido sobre colores, deportes y comida. Éstos datos están en la propiedad model. De igual manera que algunos otros componentes, tenemos un modelo de árbol para usar el JTree. Por ejemplo, cuando usamos las listas tenemos modelos de listas, pues ahora son modelos de árbol. Si no establecemos el modelo, por defecto se construye con los elementos dichos. Vamos ahora con el TreeModel.
Creando el árbol
Vamos a usar dos tipos de datos para crear la estructura y ponerla en el JTree: el DefaultTreeModel y el DefaultMutableTreeNode.
El botón para cargar el árbol lo he puesto para leer el árbol de directorios de tu disco duro. El tipo de datos TreeModel es el objeto que tiene internamente el JTree para manejar su estructura en árbol, nosotros vamos a usar una clase derivada de ésta porque se trata de una interfaz que no podemos usar directamente. Entonces tenemos el DefaultTreeModel que va a ser el tipo que usaremos.
Por otro lado tenemos que en cada elemento de un DefaultTreeModel es un DefaultMutableTreeNode. De forma que cada nodo de éstos puede tener a su vez hijos, formando así un árbol según tenga hijos o no. Para entenderlo ésto he pensado usar el árbol de directorio que todos conocemos.
Antes de seguir, explicando las dos funciones del JTree, si queremos crear una estructura de tipo árbol como por ejemplo:
…lo que tenemos que hacer es que cada nodo es un elemento de tipo DefaultMutableTreeNode, y cada elemento se puede hacer hijo de otro. Es decir, lo único que hay que decirle al programa es de cada nodo cuál es su padre con la función insertNodeInto(nodo, padre, índice). Por ejemplo para la estructura anterior programaríamos:
DefaultMutableTreeNode nodoroot, nodo1, nodo11, nodo12, nodo2, nodo21, nodo22; nodoroot = new DefaultMutableTreeNode("Éste es el nodo principal."); nodo1 = new DefaultMutableTreeNode("nodo1"); nodo11 = new DefaultMutableTreeNode("nodo11"); nodo12 = new DefaultMutableTreeNode("nodo12"); nodo2 = new DefaultMutableTreeNode("nodo2"); nodo21 = new DefaultMutableTreeNode("nodo21"); nodo22 = new DefaultMutableTreeNode("nodo22"); arbol.setRoot(nodoroot); arbol.insertNodeInto(nodo1, nodoroot, 0); arbol.insertNodeInto(nodo2, nodoroot, 1); arbol.insertNodeInto(nodo11, nodo1, 0); arbol.insertNodeInto(nodo12, nodo1, 1); arbol.insertNodeInto(nodo21, nodo2, 0); arbol.insertNodeInto(nodo22, nodo2, 1);
En el ejemplo os he dejado algo más complicado, con una función recursiva que carga toda la estructura de directorios desde el directorio raiz «/». Es decir, cuando le damos al botón tenemos el código siguiente:
btnCargarrbolDe.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) {
DefaultTreeModel arbol = (DefaultTreeModel) tree.getModel(); DefaultMutableTreeNode nroot = new DefaultMutableTreeNode("Árbol de directorios");
… una vez dicho cual es el nodo principal (nroot), la función CargaEstructuraDirectorios lo hace todo. Puede tardar bastante, depende lo que tengas en tu ordenador o lo rápido que sea, déjalo ejecutarse hasta el final si lo quieres ver el resultado.
No voy a entrar en detalle sobre funciones recursivas o lectura de directorios. Lo que hay que saber es que lista un directorio, añadiendo todo lo que encuentra al árbol en su lugar adecuado, de maner que si encuentra un directorio lo añade también. Y acto seguido entra al directorio y lo lista, de manera que si algo de dentro también es un directorio vuelve a hacer lo mismo, es decir, vuelve a entrar en éste segundo directorio y lo lista también añadiendo de nuevo los elementos en el lugar adecuado. Así sucesivamente hasta listar todos los directorios y subdirectorios.
Todo ésto queda simple con una función recursiva que os dejo aquí:
Igual que se añaden nodos, también se pueden borrar. Usando la función removeNodeFromParent que se usa en el ejemplo. Con un botón simple. No sólo podemos borrar un nodo, también podemos borrarlo de un sitio y ponerlo en otro, reordenando nuestro árbol, modificándolo, o lo que necesitemos. Pero con añadir o borrar tenemos lo básico para empezar…
Click, empieza el juego
Ahora se complica, pero empieza el juego xD ¿cómo hacer algo cuando hacemos click en un elemento del árbol? Haciendo click derecho con el ratón en el JTree añadimos el capturador de eventos que vamos a usar para hacer álgo cuando el usuario hace click en el JTree:
Eclipse, de nuevo nos genera el código esqueleto siguiente:
final JTree tree = new JTree(); tree.addTreeSelectionListener(new TreeSelectionListener() { public void valueChanged(TreeSelectionEvent e) { DefaultMutableTreeNode nseleccionado = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent(); JOptionPane.showMessageDialog(frame, nseleccionado.getPath()); } });
Se pueden capturar otros eventos. Y bueno, ya la imaginación o lo que necesitemos entra en juego para desarrollar lo que necesitemos. Con las funciones principales que nos proporcina el DefaultMutableTreeNode pordemos hacer lo que queramos. Podemos tener varios árboles y ponerlos en el JTree cuando queramos uno u otro, podemos recorrer los nodos por los índices, podemos saber cuál es el nodo principal con la función .getRoot, saber cuántos hijos tiene un nodo con .getChildCount, etcétera…
No hay más que curiosear para qué sirven las funciones proporcionadas, como podemos ver en la imagen siguiente del Eclipse:
Hay que saber que cuando hacemos cambios en la estructura de árbol, éstos automáticamente se visualizan en el JTree, con lo que sólo tenemos que centrarnos en el árbol.
Para más información me remito de nuevo a la documentación oficial:
En el ejemplo he comentado algunas cosas más. Aquí están en descarga directa los códigos fuentes. Hay un .jar, para ejecutarlo necesitas el JRE instalado, si quieres modificarlo, sólo necesitas un editor de texto ya que sólo hay un fichero .java y recompilarlo.
Todo el código comentado es el siguiente para el que no quiera descargarlo:
// La clase principal public class Principal extends JFrame {
// el panel contenedor private JPanel contentPane; // el JFrame static Principal frame;
/** * esta es la función que primero se ejecuta creando el JFRame y visualizándolo */ public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { try { frame = new Principal(); frame.setVisible(true); } catch (Exception e) { e.printStackTrace(); } } }); }
/** * la creación del JFrame principal donde está programado todo lo de éste ejemplo */ public Principal() { // título de ventana setTitle("Java Swing 8 El JTree by Jnj"); // operación al cerra el JFrame setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // dimensiones y posición en el escritorio setBounds(100, 100, 450, 306); // se crea el panel contentPane = new JPanel(); // los bordes contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); // se establece setContentPane(contentPane); contentPane.setLayout(null);
// se pone el botón en la ventana JButton btnCargarrbolDe = new JButton( "Cargar u00E1rbol de directorios"); btnCargarrbolDe.setBounds(10, 11, 200, 23); contentPane.add(btnCargarrbolDe);
// las barras de escroll para el JTree JScrollPane scrollPane = new JScrollPane(); scrollPane.setBounds(10, 45, 414, 206); contentPane.add(scrollPane);
// el JTree final JTree tree = new JTree(); // que captura el evento click tree.addTreeSelectionListener(new TreeSelectionListener() { public void valueChanged(TreeSelectionEvent e) { // se obtiene el nodo seleccionado DefaultMutableTreeNode nseleccionado = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent(); // visualiza el path del nodo JOptionPane.showMessageDialog(frame, nseleccionado.getPath()); } }); // se pone el árbol en el panel de las barras de scroll scrollPane.setViewportView(tree);
// aquí el botón que borra el último elemento de los primeros hijos // es decir, desde el nodo root, borra sólo el último hijo JButton btnBorrarltimoNodo = new JButton("Borrar u00FAltimo nodo"); btnBorrarltimoNodo.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) {
DefaultTreeModel arbol = (DefaultTreeModel) tree.getModel(); DefaultMutableTreeNode padre = (DefaultMutableTreeNode) arbol.getRoot(); int numeroDeHijos = arbol.getChildCount(padre);
// borra el último hijo del padre arbol.removeNodeFromParent((MutableTreeNode) arbol.getChild( padre, numeroDeHijos - 1)); } }); btnBorrarltimoNodo.setBounds(220, 11, 204, 23); contentPane.add(btnBorrarltimoNodo);
// evento click del botón de carga del árbol // simplemente añade el nodo root y llama a la función de carga // para añadir todos los nodos hijos al nodo root btnCargarrbolDe.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) {
DefaultTreeModel arbol = (DefaultTreeModel) tree.getModel(); DefaultMutableTreeNode nroot = new DefaultMutableTreeNode( "Árbol de directorios");
arbol.setRoot(nroot);
CargaEstructuraDirectorios(arbol, nroot, "/");
} });
}
// función recursiva que lista todos los directorios y subdirectorios // a partir de una ruta, añadiéndolos a la estructura en árbol private void CargaEstructuraDirectorios(DefaultTreeModel arbol, DefaultMutableTreeNode padre, String ruta) { DefaultMutableTreeNode aux = null;
File archivo = new File(ruta); // puntero al directorio de la ruta File[] archivos = archivo.listFiles(); // lista todos los archivos de la ruta
// recorre lo que hay en la ruta if (archivos != null) { for (int i = 0; i < archivos.length; i++) {
// creando un nodo con cada cosa del directorio aux = new DefaultMutableTreeNode(archivos[i].getName()); // inserta el nodo hijo arbol.insertNodeInto(aux, padre, i);
// si encontramos un directorio volvemos a hacer lo mismo con sus hijos if (archivos[i].isDirectory()) { try {
// llamando recursivamente de nuevo a ésta misma función CargaEstructuraDirectorios(arbol, aux, archivos[i].getAbsolutePath() + "/");
} catch (Exception e) { System.out.println(e.getMessage()); // por si acaso le he puesto un try xD } }
El tiempo pasa y no vuelve, el valor más preciado que tenemos, cuando a uno le falta es cuando lo tiene en cuenta. Estoy sacando tiempo de debajo de las piedras para seguir escribiéndoles. Vienen días de tormenta, hace mucho viento, parece que llueve pero no llueve, la prima de riesgo sube y baja, los políticos siguen con sus historias, y yo sigo con lo mío, así que aquí hoy os dejo algo más sobre Swing. En concreto sobre componentes para dar formato a textos y algo más.
En éste minitutorial, voy a dar un repaso a los componentes que sólo admiten cierto tipo de valores como los JFormattedTextField. Otro componente interesante, el JEditorPane, que admite por defecto tres tipos de contenido: el texto plano normal y corriente de siempre (text/plain), texto enriquecido (text/rtf), o un documento en HTML (text/html). Y otra caja de texto, el JTextPane, que admite incluso otros componentes Swing como etiquetas, botones, etcétera…
Para éste post he utilizado la versión JDK 7 update 9, y Eclipse Juno EE con el plugin Swing Designer.
JFormattedTextField
Éste componente hereda del JTextField, con lo que tiene los mismos métodos y se usa de manera parecida. La diferencia que tiene con respecto a los demás componentes de texto es que permite establecer una máscara de entrada para los datos que el usuario escribe. Por ejemplo si queremos poner un código postal, un número de teléfono, etcétera..
Éste componente es uno de los más prácticos porque bien programado nos aseguramos de que el usuario va a poner lo que queremos que ponga. La carencia que tiene es que no admite expresiones regulares directamente, pero se puede sortear éste problema.
Al constructor de JFormattedTextField, se le pasa un objeto nuevo de tipo MaskFormatter, y éste se encarga de usar la máscara de entrada para admitir o no los valores de entrada.
Podemos usar en la máscara los siguientes caracteres para contruir nuestra máscara:
# cualquier número. ‘ carácter especial. U carácter, que lo modifica a mayúsculas. L carácter, que lo modifica a minúsculas. A carácter o número. ? cualquier carácter. * cualquier cosa. H cualquier caracter hexadecimal, es decir, del 0 al 9 y de la ‘a’ a la ‘f’ o de la ‘A’ a la ‘F’.
También podemos comprobar con una expresión regular si el valor introducido es correcto. Tenemos un ejemplo programado, creando un objeto nuevo de tipo RegexFormatter aquí:
Si quieres construir el tuyo propio lo que debes hacer es siguiendo el siguiente código reprogramar el método stringToValue, comprobando que el texto de entrada es válido:
JFormattedTextField formattedTextField = new JFormattedTextField(new AbstractFormatter() { @Override public String valueToString(Object value) throws ParseException { // TODO Auto-generated method stub return null; }
Si quieres generar con Eclipse el código esqueleto, cuando llegues a donde pone new Abstract… le das a Ctrl + Espacio y te saldrán las opciones que tienes, ésto lo puedes hacer mientras que escribes en cualquier parte del programa para ir más rápido, entonces eliges dándole a Enter y te genera el código de aquí arriba. Ya con ésto puedes usar una expresión regular usando un ‘matcher’. Pero ésto ya sería para otro post.
Éste componente admite tres tipos de textos formateados por defecto: texto plano, documento enriquecido RTF y HTML. Un ejemplo de uso es el siguiente:
JEditorPane editorPane = new JEditorPane(); editorPane.setContentType("text/html"); editorPane.setText("<h1>Título</h1>" + "<p>Ésto <b>es</b> un <i>párrafo</i>.</p>"); editorPane.setBounds(10, 92, 481, 134); contentPane.add(editorPane);
Con la función setContentType se establece el tipo de texto, que permite: text/plain para texto normal y corriente, con text/rtf podemos poner texto enriquecido siguiendo su especificación, y el text/html para páginas web.
Éste es un subcomponente de JEditorPane, podemos usarlo exactamente igual pero además añade que podemos insertar dentro otros componentes con la función insertComponent(nombreComponente).
JTextPane textPane = new JTextPane(); textPane.setContentType("text/html"); textPane.setText("<h1>Título</h1>" + "<p>Ésto <b>es</b> un <i>párrafo</i>.</p>"); JButton boton = new JButton("Ésto es un botón"); textPane.insertComponent(boton); textPane.setBounds(10, 262, 481, 125); contentPane.add(textPane);
Se pueden añadir todo tipo de componentes, o por lo menos la declaración de insertComponent recibe un parámetro de tipo Component, con lo que habría que probar a ver si los admite todos.
Terminando
Es interesante la función setPage(URL) que admite tanto el JTextPane como el JEditorPane, con la que podemos poner una página de Internet o de la red local dentro del componente. En el ejemplo adjunto abajo se utiliza la función setPage para cargar contenido HTML. Haciendo un navegador casero.
Es interesante que también admite editar el contenido el JTextPane y el JEditorPane, con lo que hay posibilidades para hacer un editor de páginas web, por ejemplo, o de texto enriquecido. Se le pueden añadir unos botones alrededor de la ventana para cambiar los formatos.. etcétera.. ya cada uno que imagine y que haga, hay mucho juego.
Bueno con ésto ya hay para hecharle un buen vistazo a éstos componentes, espero que sirva. A continuación les dejo todo el código de ejemplo. También adjunto un fichero comprimido con todo, hay un .jar ejecutable dentro para verlo en marcha. Para ejecutarlo sólo necesitas el JRE instalado. Aquí va el código:
// éste es el panel principal que todo lo contiene private JPanel contentPane;
// función que primero se ejecuta creando el JFrame public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { try { Principal frame = new Principal(); // visualiza el frame frame.setVisible(true); } catch (Exception e) { e.printStackTrace(); } } }); }
// constructor de la clase, // se usa para crear todos los componentes y programar // todo el funcionamiento public Principal() throws ParseException {
// se configura la ventana... setTitle("Java Swing 7 Textos y otros by Jnj"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds(100, 100, 517, 438); contentPane = new JPanel(); contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); setContentPane(contentPane); contentPane.setLayout(null);
// la primera etiqueta JLabel lblJformattedtext = new JLabel("JFormattedTextField"); lblJformattedtext.setBounds(10, 11, 133, 14); contentPane.add(lblJformattedtext);
// la etiqueta del JEditorPane JLabel lblJeditorpane = new JLabel( "JEditorPane que accede a http://localhost/"); lblJeditorpane.setBounds(10, 67, 250, 14); contentPane.add(lblJeditorpane);
// la etiqueta del JTextPane JLabel lblJtextpane = new JLabel("JTextPane"); lblJtextpane.setBounds(10, 237, 109, 14); contentPane.add(lblJtextpane);
// la caja de texto formateado // con una máscara de formato JFormattedTextField formattedTextField = new JFormattedTextField( new MaskFormatter("(####)#########")); formattedTextField.setBounds(10, 36, 481, 20); contentPane.add(formattedTextField);
// el JEditorPane que usa una página web // accediendo al servidor web de nuestra // máquina local JEditorPane editorPane = new JEditorPane(); // es obligatorio usar el try catch para capturar errores try { // establece la página editorPane.setPage("http://localhost/"); } catch (IOException e) { e.printStackTrace(); } editorPane.setBounds(10, 92, 481, 134); contentPane.add(editorPane);
// y el JTextPane JTextPane textPane = new JTextPane(); // se establece el tipo textPane.setContentType("text/html"); // con un texto en HTML textPane.setText("<h1>Título</h1>" + "<p>Ésto <b>es</b> un <i>párrafo</i>.</p>"); // se crea un botón JButton boton = new JButton("Ésto es un botón"); // se inserta el botón dentro de la caja de texto textPane.insertComponent(boton); textPane.setBounds(10, 262, 481, 125); contentPane.add(textPane); } }
Hace ya un tiempo que no escribo, así que voy a tratar de que éste año sea mejor que el anterior y comienzo por avanzar en mis investigaciones con Java, así que os dejo éste post por si a alguien le sirve…
Sigo escudriñando los componentes de Swing, a ver cómo son los más simples, que no faltan en cualquier programa. Si lo que buscas es aprender rápido lo necesario para usarlos, éste es tu post xD He utilizado la versión 7 del JDK y Eclipse Juno.
Si vas a seguir éste tutorial desde cero, lo suyo sería que hicieras un nuevo proyecto, crearas un nuevo JFrame con el generador de código de Eclipse y fueras al Swing Designer. Es decir, entras al modo de diseño para editarlo intentando hacer algo como la imagen de arriba, usando las herramientas que ves aquí abajo:
Queda cada vez menos componentes de los presentados en el post ‘Java Swing 1’. Así que vamos al grano.
JLabel
Es una etiqueta. Con el diseñador ponemos el texto en el valor text, se puede cambiar en el código con la función nombreEtiqueta.setText(«Cadena»); aunque en el código fuente del ejemplo se ha puesto el texto en el constructor al usar JLabel lblEtiqueta = new JLabel(«Etiqueta»);
JTextField
Es una caja de texto. Se usa de igual manera que una etiqueta a la hora de programar o diseñar la interfaz, pero su diferencia con la etiqueta es que el usuario puede cambiar el contenido de la caja escribiendo en ella, con la etiqueta no puede hacerlo. Tendremos también la función nombre.setText(«Cadena»); anterior. Con variable = txtCajaDeTexto.getText(); podemos tener el contenido de la caja de texto en la variable. Éste componente sólo admite una línea.
JComboBox
Significa cuadro combinado, como se puede ver en el ejemplo, es una lista de elementos desplegable donde el usuario puede elegir entre las opciones que le demos. La elección la podemos tener con a función variable = comboBox.getSelectedIndex();
JButton
Es un simple botón, con el que al hacer click programamos lo que necesitemos. En el ejemplo se muestra un diálogo simple con:
btnBotn.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { JOptionPane.showMessageDialog(frame, "Ésto es un botón simple."); } });
Tambien se puede cambiar el texto con el método .setText(cadena);
JCheckBox
Las casillas de verificación, que en ejemplo hay varias. Las podemos usar de una en una, o en grupo, poniéndolas dentro de un grupo, de manera que cuando se selecciona una las demás se des-seleccionan automáticamente.
Para saber si un JCheckBox está ‘checkeado’ debemos usar la función nombreCheckBox.isSelected(); que devolverá true o false.
JRadioButton
Los botones de radio, JRadioButton para los amigos, se usan de igual manera que los JCheckBox. La diferencia es que tradicionalmente se usan para elegir una entre varias opciones, mientras que los JCheckBox normalmente se usan para elegir en varias opciones si se desean o no, sin que unas excluyan a las otras. Pero en realidad se pueden usar igual dependiendo de si los agrupamos o no. En el ejemplo se han agrupado los botones de radio con el código:
ButtonGroup radioGroup = new ButtonGroup(); radioGroup.add(rdbtn1); radioGroup.add(rdbtn2);
Ésto de aquí arriba lo que hace es que si elegimos el rdbtn1 entonces el rdbtn2 se des-selecciona y viceversa. Así de simple ocurre si también agrupamos los JCheckBox, es decir, en los elementos de un grupo sólo estará seleccionado uno.
JToggleButton
Llamado botón de activación, se usa de nuevo igual que un JCheckBox o un JRadioButton, creo que sobran las explicaciones aquí porque tenemos las mismas funciones y comportamientos, sólo cambia su apariencia por la forma de un botón que se mantiene pulsado cuando está activado.
JTextArea
Es un área de texto de varias líneas, se usa igual que el JTextField pero con la diferencia de que admite varias líneas. Podemos establecer el número de líneas con nombre.setRows(n); y tenemos también las funciones nombre.getText() y nombre.setText(). En el ejemplo se establece el texto de la forma:
txtrAreaDeTexto.setText("Arearnderntexto.");
Podemos ver que con rn se produce un salto de línea. Ésto nos puede servir en muchos otros sitios para hacer un salto de línea, así que de memoria a aprendérselo.
JPasswordField
Es un área de contraseña, es decir, un área de texto pero con la diferencia de que los caracteres no se muestran para que el usuario del ordenador de al lado no pueda leer las contraseñas que pones en tu pantalla. La diferencia con un JTextField es que para conseguir la cadena de caracteres que ha puesto el usuario hay que usar la función nombre.getPassword();
JSpinner
Ésto es una caja donde con un par de botones cambiamos los valores. Traducido se llama ‘hilandero’, otra palabra nueva para mi vocabulario, nunca la habia oido ni leido. Por defecto se configura para que recorra los enteros, si le das a la flecha arriba suma uno al valor que haya 1, 2, 3, 4, 5… Si le das abajo resta de la forma 3, 2, 1, 0, -1, -2, -3…
Con spinner.getValue(); obtenemos el valor elegido. Va a devolver un objeto del tipo correspondiente, puede ser una fecha o una lista de elementos.
Para configurarlo hay que acceder en el modo Design a la propiedad model. Internamente utiliza la función setModel() para establecer una lista de elementos que utiliza el componente. Éstos vectores son habituales en varios componentes Swing y nos van a facilitar la vida. Por resumirlos, los ListModel son vectores, y dependiendo del tipo de componente los elementos del vector van a ser de un tipo o de otro. En el JList se ha establecido uno para verlo claro.
JList
Es una lista seleccionable. Estableciendo la propiedad model pondremos los elementos fácilmente con el Swing Designer. Si vamos al código veremos que se genera un ListModel especial para el tipo JList, el AbstractListModel, y entonces podremos acceder a los elementos seleccionados con las funciones siguientes:
La lista permite selección múltiple, por eso que tenemos las funciones que devuelven vectores.
JProgressBar
Es una barra de progreso. Con nombre.setValue(nuevoValor) y nombre.getValue() podemos cambiar o saber el valor que tiene en cada momento. Su manejo es sencillo y pienso que con esas dos funciones en bastante para empezar con ello.
JScrollBar
Es una barra de desplazamiento. Se usa igual que la barra de progreso a la hora de programar, pero añade que el usuario puede moverla, de manera que puede cambiar el valor de su posición. Con ésto que añade es interesante capturar el evento para hacer algo cuando el usuario cambia su posición, para ello con el boton derecho en el componente la damos al evento de cambio de ajuste:
En el código del ejemplo se puede ver que cuando se mueve la barra de desplazamiento se van a ajustar tanto la barra la progreso como el deslizador que viene a continuación.
JSlider
En castellano se llama deslizador. Funciona igual que la barra de desplazamiento, pero nos permite un mayor ajuste, pudiendo mostrar una regla con los números. Para que se vea la regla debemos poner los valores que se ven en la imagen siguiente:
Si no configuramos éstos valores en el editor de diseño no se verán los números. De igual manera que los anteriores, con getValue() tendremos el valor actual.
En la barra de desplazamiento se ha capturado el evento que se dispara al cambiar el ajuste. Para el deslizador el evento que se dispara cuando se mueve el deslizador se llama cambio de estado, como se ve en el código fuente:
Simplemente es una línea de separación, una raya, que ya puse en el post anterior sobre los menús. Se puede poner también en el JFrame, concretamente en el panel, para dibujar una separación.
Terminando
Todo lo anterior se puede ver al detalle escudriñando un poco el código fuente generado con el Swing Designer y un poco programado manualmente:
// la función que primero se ejecuta que crea // el frame en memoria... public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { try { frame = new Principal(); // ... y lo visualiza frame.setVisible(true); } catch (Exception e) { e.printStackTrace(); } } }); }
// aquí está el constructor de la clase que todo // lo programa... public Principal() { // se contruye la ventana setTitle("JavaSwing 6 Componentes bu00E1sicos by Jnj"); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds(100, 100, 458, 363); contentPane = new JPanel(); contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); setContentPane(contentPane); contentPane.setLayout(null);
// la etiqueta JLabel lblEtiqueta = new JLabel("Etiqueta"); lblEtiqueta.setBounds(10, 11, 67, 14); contentPane.add(lblEtiqueta);
// la caja de texto txtCajaDeTexto = new JTextField(); txtCajaDeTexto.setText("Caja de texto"); txtCajaDeTexto.setBounds(10, 36, 152, 20); contentPane.add(txtCajaDeTexto); txtCajaDeTexto.setColumns(10);
// el cuadro combinado JComboBox comboBox = new JComboBox(); // los elementos comboBox.setModel(new DefaultComboBoxModel(new String[] {"Elemento 1", "Elemento 2", "Elemento 3"})); // comienza seleccionado el elemento 1 comboBox.setSelectedIndex(1); comboBox.setBounds(10, 67, 191, 20); contentPane.add(comboBox);
// el botón JButton btnBotn = new JButton("Botu00F3n"); // cuando se pulsa hace ésto btnBotn.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { JOptionPane.showMessageDialog(frame, "Ésto es un botón simple."); } }); btnBotn.setBounds(53, 95, 89, 23); contentPane.add(btnBotn);
// la casilla de verificación JCheckBox chckbx = new JCheckBox("Casilla de verificaciu00F3n"); chckbx.setSelected(true); chckbx.setBounds(10, 125, 191, 23); contentPane.add(chckbx);
// los botones de radio JRadioButton rdbtn1 = new JRadioButton("RadioButton 1"); rdbtn1.setBounds(10, 151, 109, 23); contentPane.add(rdbtn1);
// el grupo para los botones ButtonGroup radioGroup = new ButtonGroup(); radioGroup.add(rdbtn1); radioGroup.add(rdbtn2);
// otras casillas de verificación que se van a agrupar // para ver el comportamiento JCheckBox chckbx1 = new JCheckBox("CheckBox 1 agrupado"); chckbx1.setBounds(10, 203, 166, 23); contentPane.add(chckbx1);
// el grupo para las casillas de verificación ButtonGroup chkGroup = new ButtonGroup(); chkGroup.add(chckbx1); chkGroup.add(chckbx2);
// el botón de activación JToggleButton tglbtn = new JToggleButton("Botu00F3n de activaciu00F3n"); // que está seleccionado inicialmente tglbtn.setSelected(true); tglbtn.setBounds(10, 259, 166, 23); contentPane.add(tglbtn);
// el área de texto JTextArea txtrAreaDeTexto = new JTextArea(); // con una linea en el borde txtrAreaDeTexto.setBorder(new LineBorder(new Color(0, 0, 0))); // tres lineas de texto permitidas txtrAreaDeTexto.setRows(3); // el texto txtrAreaDeTexto.setText("Arearnderntexto."); txtrAreaDeTexto.setBounds(224, 11, 205, 58); contentPane.add(txtrAreaDeTexto);
// el texto de contrasela passwordField = new JPasswordField(); passwordField.setBounds(224, 80, 152, 20); // contraseña inicial passwordField.setText("supercontraseña"); contentPane.add(passwordField);
// el llamado 'hilandero' JSpinner spinner = new JSpinner(); spinner.setBounds(224, 111, 101, 20); contentPane.add(spinner);
// la lista JList list = new JList(); // la declaración de los elementos list.setModel(new AbstractListModel() { String[] values = new String[] {"Elemento 1", "Elemento 2", "Elemento 3"}; // función que deveuelve el número de elementos public int getSize() { return values.length; } // función para acceder a cada elemento // por ejemplo usando // // list.getModel().getElementAt(1) // // nos devolverá el elemento "Elemento 2" public Object getElementAt(int index) { return values[index]; } }); list.setSelectedIndex(1); list.setBounds(224, 142, 67, 52); contentPane.add(list);
// el separador JSeparator separator = new JSeparator(); separator.setOrientation(SwingConstants.VERTICAL); separator.setBounds(211, 11, 2, 297); contentPane.add(separator);
// inicializacion de la barra de progreso, // la barra de desplazamiento y el deslizador. // los he puesto aquí juntos porque los uso // después y era necesario. final JProgressBar progressBar = new JProgressBar(); final JScrollBar scrollBar = new JScrollBar(); final JSlider slider = new JSlider();
// configuraciones de la barra de progreso progressBar.setValue(50); progressBar.setBounds(224, 203, 205, 14); contentPane.add(progressBar);
// la barra de desplazamiento scrollBar.addAdjustmentListener(new AdjustmentListener() { public void adjustmentValueChanged(AdjustmentEvent arg0) { // mueve a la vez la barra de progreso y el deslizador progressBar.setValue(scrollBar.getValue()); slider.setValue(scrollBar.getValue()); } }); // valor inicial scrollBar.setValue(50); scrollBar.setOrientation(JScrollBar.HORIZONTAL); scrollBar.setBounds(224, 233, 205, 23); contentPane.add(scrollBar);
// el deslizador, capturando evento slider.addChangeListener(new ChangeListener() { public void stateChanged(ChangeEvent arg0) { // mueve la barra de progreso y la de desplazamiento // cada vez que se mueve el deslizador. progressBar.setValue(slider.getValue()); scrollBar.setValue(slider.getValue()); } }); // valores para que salga la regla numerada slider.setMinorTickSpacing(1); slider.setMajorTickSpacing(10); slider.setToolTipText(""); slider.setPaintTicks(true); slider.setPaintLabels(true); slider.setBounds(224, 267, 200, 52); contentPane.add(slider);
} // termina el constructor de la ventana } // termina la clase
Un fichero .zip para descargar con todo el código fuente y un ejecutable .jar para verlo en acción lo tienen aquí: descargar ejemplo.
Para ejecutarlo es necesario por lo menos tener el JRE, recomiendo la última versión a ser posible, la 7.
Como viene siendo habitual, para más información, me remito a la documentación oficial:
Estoy pegándole un repaso a todo lo referente a los menús con Swing usando Eclipse. No deja de sorprenderme lo fácil y rápido que lo tenemos una vez que te has familiarizado con Eclipse, y en concreto con el Swing Designer. En pocos minutos tenemos nuestro esqueleto de la aplicación listo para codificar sus funciones o encajarlo con códigos ya hechos, cada uno para lo que lo necesite…
Para todo ésto he usado la zona del Swing Designer que vemos a continuación:
Jugando con los componentes donde pone ‘Menu’ podemos hacer todo lo referente. Un JMenuBar es una barra de menús, es la típica barra de menús que sale normalmente en la zona de arriba de un programa con Archivo, Opciones, Ayuda, etcétera… Tenemos que poner uno en el JFrame para entonces, dentro de éste JMenuBar, podamos poner los JMenu o JMenuItem que queramos. Dentro de un JMenu podemos poner otros JMenu o elementos finales de tipo JMenuItem, JCheckBoxMenuItem, JRadioButtonMenuItem o en especial el JSeparator que simplemente es una línea separadora como la siguiente después de ‘Poner título’:
Luego tenemos otro tipo de menú que es el JPopupMenu. Se usa igual que el JMenuBar pero se pone en un panel en donde podremos hacer click derecho y saldrá el menú desplegable con los elementos que hayamos hecho.
Para hacer la estructura del ejemplo he puesto los siguientes componentes todos dentro del JMenuBar:
Archivo (JMenu) – Poner título (JMenuItem) – Barra separadora (JSeparator) – Un JCheckBoxMenuItem (JCheckBoxMenuItem) – Un JRadioButtonMenuItem (JRadioButtonMenuItem) – Otro JRadioButtonMenuItem (JRadioButtonMenuItem) – Salir (JMenuItem) Colores (JMenuItem) – Blanco (JMenuItem) – Negro (JMenuItem) – Cambiar el color de fondo con otros colores (JMenu) — Verde (JMenuItem) — Rojo (JMenuItem) — Azul (JMenuItem) Minimizar (JMenuItem)
En el JPopupMenu que pone ¡Hola! ¡Feliz Navidad! simplemente hay dos JMenuItem. En medio de la ventana he puesto un JSeparator para mostrar que se puede poner no sólo como un elemento de menú. Y luego tenemos un JLabel con la frase explicatoria.
Sólo hay que ponerse a juguetear con el Swing Designer y seguro que bien rápido que se le coge el truco.
Queda añadir cómo hacer que haga algo al hacer click, ésto se llama ponerle un manejador de evento, en éste caso al evento click del ratón. De nuevo usando el Swing Designer, que para eso está, para hacernos la vida más fácil xD le damos en cualquier componente Swing con el botón derecho y nos sale un menú desplegable como el siguiente:
En la opción ‘Add event handler’ tenemos todos los eventos disponibles que podemos asociar con casi cualquier componente. El que nos interesa ahora es el mousePressed que significa apretar el ratón, que en la imagen de arriba ya sale programado, por eso pone -> line 110, porque está en la línea 110 del código fuente. Ésto lo hacemos para cada cosa que queramos que haga algo al hacer click y al darle a mousePressed nos dirigirá al código para que lo programemos, creando el esqueleto del programa asociado a dicho evento del ratón.
Los elementos JCheckBoxMenuItem y JRadioButtonMenuItem también tienen el evento del click de ratón, y podemos usar una propiedad llamada ‘selected’ para saber si estan seleccionados o no y hacer lo correspondiente en cada caso.
Aquí tienes el código del ejemplo:
// el paquete del programa
package principal;
// las librerias de java para hacer todo...
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Frame;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JSeparator;
import javax.swing.border.EmptyBorder;
// la clase principal
public class Principal extends JFrame {
// el panel que todo lo contiene
private JPanel contentPane;
// la entrada al programa, lo que primero
// se ejecuta
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
// crea el frame en memoria
// y lo visualiza
Principal frame = new Principal();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
// ésto es el constructor de la clase
// aquí está todo
public Principal() {
// al cerrar la ventana sale del programa
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// dimensiones y posición al arrancar del JFrame
setBounds(100, 100, 450, 300);
// la barra de menú
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
// el menú Archivo empieza aquí ---------------------------------------
JMenu mnArchivo = new JMenu("Archivo");
menuBar.add(mnArchivo);
JMenuItem mntmPonerTtulo = new JMenuItem("Poner tu00EDtulo");
mntmPonerTtulo.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
setTitle("Java Swing 5: Menús by jnj");
}
});
mnArchivo.add(mntmPonerTtulo);
JMenuItem mntmSalir_1 = new JMenuItem("Salir");
mntmSalir_1.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
dispose();
}
});
JSeparator separator_1 = new JSeparator();
mnArchivo.add(separator_1);
JCheckBoxMenuItem chckbxmntmUnJcheckboxmenuitem = new JCheckBoxMenuItem(
"Un JCheckBoxMenuItem");
mnArchivo.add(chckbxmntmUnJcheckboxmenuitem);
JRadioButtonMenuItem rdbtnmntmUnJradiobuttonmenuitem = new JRadioButtonMenuItem(
"Un JRadioButtonMenuItem");
mnArchivo.add(rdbtnmntmUnJradiobuttonmenuitem);
JRadioButtonMenuItem rdbtnmntmOtroJradiobuttonmenuitem = new JRadioButtonMenuItem(
"Otro JRadioButtonMenuItem");
mnArchivo.add(rdbtnmntmOtroJradiobuttonmenuitem);
mnArchivo.add(mntmSalir_1);
// el menú Colores empieza aquí ---------------------------------------
JMenu mnColores = new JMenu("Colores");
menuBar.add(mnColores);
JMenuItem mntmBlanco = new JMenuItem("Blanco");
mntmBlanco.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
contentPane.setBackground(Color.WHITE);
}
});
mnColores.add(mntmBlanco);
JMenuItem mntmNegro = new JMenuItem("Negro");
mntmNegro.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
contentPane.setBackground(Color.BLACK);
}
});
mnColores.add(mntmNegro);
JMenu mnCambiarElColor = new JMenu(
"Cambiar el color de fondo con otros colores");
mnColores.add(mnCambiarElColor);
JMenuItem mntmVerde = new JMenuItem("Verde");
mntmVerde.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
contentPane.setBackground(Color.GREEN);
}
});
mnCambiarElColor.add(mntmVerde);
JMenuItem mntmRojo = new JMenuItem("Rojo");
mntmRojo.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
contentPane.setBackground(Color.RED);
}
});
mnCambiarElColor.add(mntmRojo);
JMenuItem mntmAzul = new JMenuItem("Azul");
mntmAzul.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
contentPane.setBackground(Color.BLUE);
}
});
mnCambiarElColor.add(mntmAzul);
// el elemento del menú para minimizar está aquí ---------------------------------------
JMenuItem mntmNewMenuItem = new JMenuItem("Minimizar");
menuBar.add(mntmNewMenuItem);
mntmNewMenuItem.addMouseListener(new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
setState(Frame.ICONIFIED);
}
});
// aquí termina la barra de menú ...
// el panel que todo lo contiene se crea
contentPane = new JPanel();
// se le pone un borde
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
// se establece en el JFrame
setContentPane(contentPane);
// se hace JPopupMenu y lo pongo en el panel contenedor
JPopupMenu popupMenu = new JPopupMenu();
popupMenu.setBounds(0, 0, 319, 58);
addPopup(contentPane, popupMenu);
// el primer elemento del desplegable
JMenuItem mntmhola = new JMenuItem("u00A1Hola!");
popupMenu.add(mntmhola);
// el segundo elemento del desplegable
JMenuItem mntmfelizNavidad = new JMenuItem("u00A1Feliz Navidad!");
popupMenu.add(mntmfelizNavidad);
// el resto del JPanel con la raya separadora y la etiqueta explicatoria
contentPane.setLayout(null);
JSeparator separator = new JSeparator();
separator.setBounds(31, 88, 372, 24);
contentPane.add(separator);
JLabel lblHazClickCon = new JLabel(
"Haz click con el botu00F3n derecho en la ventana para que salga el menu00FA.");
lblHazClickCon.setBounds(10, 118, 414, 76);
contentPane.add(lblHazClickCon);
}
// ésto es lo relativo al menú desplegable
// lo que hace es mostrar éste menú donde sea que hagamos click
private static void addPopup(Component component, final JPopupMenu popup) {
component.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
if (e.isPopupTrigger()) {
showMenu(e);
}
}
public void mouseReleased(MouseEvent e) {
if (e.isPopupTrigger()) {
showMenu(e);
}
}
private void showMenu(MouseEvent e) {
popup.show(e.getComponent(), e.getX(), e.getY());
}
});
}
}
Para hacer un programa con pestañas repartiendo así los componentes se hace fácil con Swing y Eclipse. En pocos minutos, usando el generador de código esqueleto y con el Swing Designer te genera código.
Antes de poner el código, a saber, con el Swing Designer lo que tendremos será la ventana vacía, para poder posicionar los elementos tenemos que añadir en medio un Absolute layout. Luego dibujamos donde va a ir con el JTabbedPane. Después tenemos que añadir dos paneles encima del JTabbedPane, es decir, dos JPanel normales y corrientes. Las pestañas las creará el JTabbedPane cuando añadimos los paneles. Entonces ya tenemos las pestañas creadas, pero tenemos que decirle, en cada pestaña cómo vamos a posicionar los elementos y ponemos otros Absolute layout, uno en cada pestaña. Ahroa ya tenemos las pestañas y además podremos poner los elementos en la zona de la pestaña en que queramos, que en éste caso es una etiqueta JLabel en cada pestaña.
Aquí está el código:
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
import javax.swing.border.EmptyBorder;
// Ésta es la clase principal
// es un JFrame...
public class Principal extends JFrame {
// el panel principal donde se pone todo
private JPanel contentPane;
// ejecuta primero todo ésto
public static void main(String[] args) {
// arranca en un hilo de ejecución...
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
// creando el frame y lo muestra
Principal frame = new Principal();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
// Constructor de la clase: se configura aquí toda
// la ventana y los controles...
public Principal() {
// título de la ventana
setTitle("Pestau00F1as con Swing by jnj");
// operación al cerra la ventana
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// coordenadas de las esquinas del frame en el escritorio
setBounds(100, 100, 419, 234);
// el panel que contiene todo se crea y se pone en el frame
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
setContentPane(contentPane);
// distribución nula para poder posicionar los elementos
// en las coordenadas que queramos
contentPane.setLayout(null);
// se crea el panel de pestañas
JTabbedPane panelDePestanas = new JTabbedPane(JTabbedPane.TOP);
// se posiciona en el panel
panelDePestanas.setBounds(10, 11, 383, 174);
contentPane.add(panelDePestanas);
// éste es el primer panel
// que se añade como pestaña al 'tabbedPane'
JPanel panel1 = new JPanel();
panelDePestanas.addTab("Panel 1", null, panel1, null);
// al panel le pongo distribución nula para
// posicionar los elementos en las coordenadas que
// quiera
panel1.setLayout(null);
// una etiqueta en el panel de la pestaña 1
JLabel lbl1 = new JLabel("Primera pestau00F1a..");
lbl1.setBounds(10, 11, 348, 14);
panel1.add(lbl1);
// otro panel de igual forma
JPanel panel2 = new JPanel();
panelDePestanas.addTab("Panel 2", null, panel2, null);
panel2.setLayout(null);
// otra etiqueta ésta vez en el segundo panel
JLabel lbl2 = new JLabel("Segunda pestau00F1a..");
lbl2.setBounds(10, 11, 290, 14);
panel2.add(lbl2);
}
}
Ésto son un JFrame, un JDialog y un JInternalPane que van por un escritorio, y dice el JDialog al JFrame:
– ¡El usuario no me hace caso!
Interviene el JInternalFrame y le dice:
– Yo lo tengo chungo, ¡¡hazte modal pues!!
…
Qué chiste más friki xDD Bromas aparte.
En éste post voy a escribir sobre el manejo de las ventanas más básicas. Crear, abrir, cerrar, mostrar, esconder, cuádros de diálogo, ventanas internas o externas. Con el diseñador de ventanas (el Swing Designer), puedes ir construyendo tu propio ejemplo. Luego dejaré el ejemplo, códigos fuentes, con comentarios explicando los pasos.
Tres clases de ejemplo
Para el ejemplo he usado tres clases que he llamado:
EjemploJFrame
EjemploJDialog
EjemploJInternalFrame
Lo primero a hacer es tener la última versión de Eclipse y de Java, a ser posible. A fecha de hoy tengo la versión juno de Eclipse y de Java la 1.7 update 9. Y creo un nuevo proyecto llamado JavaSwing3JugandoConVentanas.
Gran parte del código lo ha generado Eclipse, así que me he ahorrado mucho tiempo. En el explorador de proyectos, con el botón derecho, en la carpeta donde van a ir los código, le he dado a New… -> Other…
Me ha salido una ventana como la de la imagen de arriba, he hecho las tres clases con los nombre que he puesto antes y ya tengo listo el esqueleto del programa. He puesto las clases en el paquete por defecto, es decir, sin poner el package donde quiero que esten, por eso en el código no se ve la línea package…
La idea de éste tutorial es intentar hacer un programa como el de la primera imagen del post, mirando lo menos posible los códigos fuentes de abajo.
Un JFrame es la ventana pricipal del programa, debe haber sólo uno. Éste tiene icono, y sale en la barra de tareas. Sin embargo el JDialog pertenece a un JFrame, debe tener un padre, no tiene icono ni sale en la barra de tareas. Por otro lado el JInternalFrame corre dentro de otro panel, en éste caso he puesto el caso más habitual, que corra dentro de un JDesktopPane. Se pueden abrir muchos diálogos y también muchas ventanas internas. Las ventanas modales, impiden que el usuario toque nada hasta que termine en ésa ventana y la cierre. En líneas generales ésto son las diferencias.
El código siguiente hace todo ésto, piensa que casi todo el código me lo ha generado Eclipse. He tratado de resumir el ejemplo lo más posible, comentandolo, aquí está el código:
EjemploJFrame
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.border.EmptyBorder;
public class EjemploJFrame extends JFrame {
private JPanel contentPane;
// METODO PRINCIPAL QUE ARRANCA TODA LA APLICACIÓN
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
// crea el JFrame
EjemploJFrame frame = new EjemploJFrame();
// lo hace visible
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
// EL CONSTRUCTOR QUE SE USA CUANDO ARRANCA AQUÍ ARRIBA
public EjemploJFrame() {
// el titulo de la ventana
setTitle("Swing: Jugando con las ventanas by jnj");
// acción al cerrar el frame: sale del programa
// en éste caso, terminando la ejecución
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// posición de las esquinas del formulario en tu escritorio
setBounds(100, 100, 450, 433);
// el panel que contiene todo
contentPane = new JPanel();
// borde del panel
contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
// se pone el panel en la ventana
setContentPane(contentPane);
// se establece disposición de las capas a null
// para poder posicionar por coordenadas los componentes
// ésto se hace con un Absolute Layout que se llama
// en el diseñador visual
contentPane.setLayout(null);
// el botón que abre ventanas de diálogo
JButton btnAbrirUnaVentana = new JButton(
"Abrir una ventana de diu00E1logo con el usuario");
btnAbrirUnaVentana.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
EjemploJDialog dialogo = new EjemploJDialog();
dialogo.setVisible(true);
}
});
btnAbrirUnaVentana.setBounds(10, 11, 414, 23);
contentPane.add(btnAbrirUnaVentana);
// el botón que abre ventanas de diálogo
// pero de manera MODAL
JButton btnAbrirUnaVentana_1 = new JButton(
"Abrir una ventana de diu00E1logo MODAL");
btnAbrirUnaVentana_1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
EjemploJDialog dialogo = new EjemploJDialog();
dialogo.setModal(true);
dialogo.setVisible(true);
}
});
btnAbrirUnaVentana_1.setBounds(10, 70, 414, 23);
contentPane.add(btnAbrirUnaVentana_1);
JLabel lblSiLaAbrimos = new JLabel(
"Si la abrimos modal, hasta no cerrarla no podemos tocar u00E9sta ventana.");
lblSiLaAbrimos.setBounds(10, 45, 414, 14);
contentPane.add(lblSiLaAbrimos);
// el panel-escritorio donde veremos los JFrames internos
final JDesktopPane desktopPane = new JDesktopPane();
desktopPane.setBounds(10, 138, 414, 246);
contentPane.add(desktopPane);
// botón para abrir las ventanas internas
JButton btnAbrirUnJframe = new JButton(
"Abrir un JFrame interno aquu00ED abajo");
btnAbrirUnJframe.addActionListener(new ActionListener() {
// variable para que cada nueva ventana interna
// salga en diferente posición y se vean todas
int posicioninicial = 0;
public void actionPerformed(ActionEvent e) {
EjemploJInternalFrame jif = new EjemploJInternalFrame();
jif.setVisible(true);
// posición de la nueva ventana
jif.setLocation(posicioninicial, posicioninicial);
// las siguiente posición será diferente así
// veremos todas las ventanas internas
posicioninicial += 10;
desktopPane.add(jif);
}
});
btnAbrirUnJframe.setBounds(10, 104, 414, 23);
contentPane.add(btnAbrirUnJframe);
}
}
EjemploJDialog
import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.border.EmptyBorder;
// AQUÍ EMPIEZA LA CLASE
public class EjemploJDialog extends JDialog {
private final JPanel contentPanel = new JPanel();
/* ESTO LO GENERA ECLIPSE PARA PROBAR MIENTRAS CONSTRUIMOS
* EL DIALOGO, LO PODEMOS DESCOMENTAR PARA
* PROBAR EL DIÁLOGO SIN PROBAR TODA LA APLICACIÓN COMPLETA
public static void main(String[] args) {
try {
EjemploJDialog dialog = new EjemploJDialog();
dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
dialog.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}*/
// CONSTRUCTOR DE LA CLASE
// crea la ventana, con los bordes, botones,
// y todos los componentes internos para hacer lo que
// se pretenda con éste diálogo.
public EjemploJDialog() {
// evita cambio de tamaño
setResizable(false);
// título del diáolog
setTitle("u00C9sto es una ventana de diu00E1logo");
// dimensiones que ocupa en la pantalla
setBounds(100, 100, 450, 229);
// capa que contendrá todo
getContentPane().setLayout(new BorderLayout());
// borde de la ventan
contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5));
// pone el panel centrado
getContentPane().add(contentPanel, BorderLayout.CENTER);
// sin capas para poder posicionar los elementos por coordenadas
contentPanel.setLayout(null);
{
// aquí se pone el JTextArea dentro de un JScrollPane
// para que tenga barras de desplazamiento
JScrollPane scrollPane = new JScrollPane();
scrollPane.setBounds(10, 11, 424, 146);
contentPanel.add(scrollPane);
{
JTextArea txtrstoEsUn = new JTextArea();
txtrstoEsUn.setText("u00C9sto es un JTextArea, aquu00ED podemos poner un texto de varias lu00EDneas.rn1rn2rn3rn..rnrnObserva que no se ve en la barra de tareas que exista u00E9sta ventana. Si fuera un JFrame su00ED que se veru00EDa en la barra de tareas con el texto del tu00EDtulo de la ventana...rnrnEl componente JTextArea estu00E1 dentro de un JScrollPane para que se visualizen las barras de scroll cuando sea necesario.rnrnLa ventana tiene el atributo 'resizable' a 'false' para evitar que se pueda cambiar el tamau00F1o.rnrnrnrnrnrnFin del texto.");
txtrstoEsUn.setLineWrap(true);
txtrstoEsUn.setAutoscrolls(true);
scrollPane.setViewportView(txtrstoEsUn);
}
}
{
// a continuación tenemos los botones clásicos 'Vale' y 'Cancela'
// éste código lo ha generado Eclipse...
JPanel buttonPane = new JPanel();
buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT));
getContentPane().add(buttonPane, BorderLayout.SOUTH);
{
JButton okButton = new JButton("Vale");
okButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// aquí van las acciones al hacer click en Vale
// envía el diálogo al recolector de basura de Java
dispose();
}
});
okButton.setActionCommand("Vale");
buttonPane.add(okButton);
getRootPane().setDefaultButton(okButton);
}
{
JButton cancelButton = new JButton("Cancelar");
cancelButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
// aquí van las acciones al hacer click en Vale
// envía el diálogo al recolector de basura de Java
dispose();
}
});
cancelButton.setActionCommand("Cancelar");
buttonPane.add(cancelButton);
}
}
}
}
EjemploJInternalFrame
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
public class EjemploJInternalFrame extends JInternalFrame {
/* ÉSTE MAIN LO GENERA ECLIPSE PARA PRUEBAS, LO DEJO
* COMENTADO PORQUE YA ESTÁ FUNCIONANDO Y SE EJECUTARÁ
* DENTRO DE UN JDesktopPane COMO TODO BUEN JInternalFrame
* TAMBIÉN SE PUEDE EJECUTAR DENTRO DE OTROS PANELES
* PERO ESO NO SE SUELE UTILIZAR.
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
EjemploJInternalFrame frame = new EjemploJInternalFrame();
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}*/
// ÉSTE ES EL CONSTRUCTOR DE LA CLASE
public EjemploJInternalFrame() {
// coordenadas con las dimensiones de la ventana
setBounds(100, 100, 245, 134);
// disposición de capas a nulo para poder posicionar
// con coordenadas en vez de con el esquema determinado
getContentPane().setLayout(null);
// un etiqueta...
JLabel lblstoEsUna = new JLabel("u00C9sto es una ventana interna...");
lblstoEsUna.setBounds(10, 11, 333, 14);
getContentPane().add(lblstoEsUna);
// el botón que cierra ésta ventana interna
JButton btnAprietaAquPara = new JButton("Aprieta aquu00ED para cerrar");
btnAprietaAquPara.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
// ésto se ejecuta cuando se aprieta el botón
// manda la clase a la basura,
// con lo que se cierra la ventana
dispose();
}
});
btnAprietaAquPara.setBounds(10, 36, 209, 58);
getContentPane().add(btnAprietaAquPara);
}
}
Terminando
Puedes copiar éstos códigos cada uno en su fichero .java correspondiente: EjemploJFrame.java, EjemploJDialog.java y EjemploJInternalFrame.java. El fichero que arranca toda la aplicación es EjemploJFrame.java. Sólo necesitas el JRE para ejecutarlo.
Me volví un poco loco aprendiendo ésto, así que si te he aclarado las ideas, o has aprendido algo, estoy satisfecho.
Para éste microtutorial he usado el Swing Designer de Eclipse y la versión 1.7 update 9 de Java, el JDK y el JRE.
En Eclipse tenemos tenemos varias opciones para generar código esqueleto para nuestro programa. En la ventana del explorador de proyectos, en el lugar elegido le damos con el botón derecho a New -> Other… y nos saldrá una ventana como la siguiente con las opciones disponibles con respecto a Swing:
En el árbol de Swing Designer tenemos las opciones para crear programas con ventanas Swing, valga la redundancia. Todos los códigos generados con el Swing Designer también se pueden crear a mano y se pueden llevar a otros entornos de desarrollo y te van a funcionar. Ésto lo digo porque no ocurre siempre, según en qué entorno de desarrollo estés, a veces pasa que si te sales de él ya no te sirven los códigos porque se les han puesto «extras» que te atan a ellos. Ésto no te va a pasar con los Swing en Eclipse, o por lo menos yo no me encontrado con éste problema.
Vamos al grano…
Application Window
Si elegimos una Application Window nos va a crear la clase siguiente:
// primero se ejecuta ésto… public static void main(String[] args) { // en un hilo de ejecución aparte hace todo // porque todo lo hace en un new Runnable // a continuación… EventQueue.invokeLater(new Runnable() { // run es el método que se ejecuta // al arrancar el hilo public void run() { try { // crea una variable de la clase y se inicializa // con el constructor de la misma ApplicationWindowPrueba window = new ApplicationWindowPrueba(); // muestra la ventana window.frame.setVisible(true); } catch (Exception e) { e.printStackTrace(); } } }); }
// éste es el constructor de la clase public ApplicationWindowPrueba() { initialize(); }
// la función que inicializa private void initialize() { // crea el frame frame = new JFrame(); // coordenadas de la ventana frame.setBounds(100, 100, 450, 300); // establece la operación a ejecutar cuando // se cierra el JFrame frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); }
}
Se trata de una clase que no hereda de ninguna otra, y lo que simplemente hace es crear un JFrame en un hilo de ejecución separado.
JApplet
Un Applet es una programa de Java que se ejecuta en un navegador de Internet. Éste programa se le envía al cliente mientras que navega y se ejecuta en su máquina. Nos crea lo básico para hacer uno, pero a fecha de hoy están totalmente en desuso y los navegadores los desactivan por defecto.. Así que mejor ni probarlo…
package pruebasSwing;
import javax.swing.JApplet;
// clase que extiende de JApplet, // que a su vez extiende de la clase // Applet y ésta en sí es un Panel… public class JAppletPrueba extends JApplet {
// el constructor public JAppletPrueba() {
} }
JDialog
Con ésta opción generaremos el código para hacer un típico cuadro de diálogo con el usuario para que se le informe de algo o elija entre las opciones que queramos. Éste tipo de ventanas se usan asociadas a otra ventana padre, aunque al tener un main se puede generar un .jar ejecutable que sólo tenga un cuadro de diálogo de éstos.
// la clase que extiende de JDialog, // que a su vez extiende de Dialog, // y ésta de Window… public class JDialogPrueba extends JDialog {
private final JPanel contentPanel = new JPanel();
public static void main(String[] args) { try { // crea un nuevo objeto de la clase JDialogPrueba dialog = new JDialogPrueba(); // operacion al cerrar dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE); // lo muestra dialog.setVisible(true); } catch (Exception e) { e.printStackTrace(); } }
// constructor de la clase public JDialogPrueba() { // propiedades de la ventana setBounds(100, 100, 450, 300); getContentPane().setLayout(new BorderLayout()); contentPanel.setLayout(new FlowLayout()); contentPanel.setBorder(new EmptyBorder(5, 5, 5, 5)); getContentPane().add(contentPanel, BorderLayout.CENTER);
// los botones para confirmar o cancelar { JPanel buttonPane = new JPanel(); buttonPane.setLayout(new FlowLayout(FlowLayout.RIGHT)); getContentPane().add(buttonPane, BorderLayout.SOUTH); { JButton okButton = new JButton(«OK»); okButton.setActionCommand(«OK»); buttonPane.add(okButton); getRootPane().setDefaultButton(okButton); } { JButton cancelButton = new JButton(«Cancel»); cancelButton.setActionCommand(«Cancel»); buttonPane.add(cancelButton); } } }
}
JFrame
Con el asistente del JFrame, hace casi lo mismo que con el de Application Window, pero hay dos ligeras diferencias. La primera es que con Application Window tenemos una variable interna a la clase que es el JFrame, y con el éste lo que tenemos es que la clase misma extiende a JFrame, con lo que realmente tenemos un JFrame en sí que se puede reutilizar en otros lugares del programa. La segunda diferencia es que añade un JPanel al JFrame, cosa que con el asistente de Application Window no lo hace.
public JFramePrueba() { setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setBounds(100, 100, 450, 300); // añade el panel… contentPane = new JPanel(); contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); contentPane.setLayout(new BorderLayout(0, 0)); setContentPane(contentPane); }
}
JInternalFrame
Ésta clase se usa para hacer ventanas que se ejecutan dentro de un JDesktopPane. Es decir, dentro de la misma ventana padre, tendremos una zona que será un panel de escritorio, el anterior JDesktopPane. Y en éste panel es donde veremos el JInternalFrame. Así que necesitaremos un JFrame padre para poder ejecutarlo dentro.
Ésta clase tiene un main pero no se puede ejecutar en un .jar aparte.
public class JInternalFramePrueba extends JInternalFrame {
// arranca en un hilo el formulario hijo public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { public void run() { try { JInternalFrame frame = new JInternalFrame(); frame.setVisible(true); } catch (Exception e) { e.printStackTrace(); } } }); }
// aquí estará construida la ventana public JInternalFramePrueba() { setBounds(100, 100, 450, 300);
}
}
JPanel
Ésto es una clase básica que podemos usar en otros formularios o ventanas. Es decir, podemos poner paneles dentro de JFrames. Así que puede ser útil tener un panel programado aparte. El código que genera es:
package pruebasSwing;
import javax.swing.JPanel;
public class JPanelPrueba extends JPanel {
// constructor de la clase public JPanelPrueba() {
}
}
Swing Automatic Databinding
Aquí tenemos un asistente que nos va a crear lo necesario para crear una ventana con Databindings. Necesitaremos un Bean, y configurar las diversas opciones. Ésto es bastante extenso así que ya tengo tema para otro post.. 🙂
Terminando
Las principales clases que primero se van a usar van a ser la ApplicationWindow o la JFrame. De éstas la JFrame es bueno usarla de un principio porque luego la puede incluir dentro de otros proyectos sin muchos cambios.
Dentro de Eclipse podemos visualizar todos los anteriores con F11, menos el JInternalFrame y el JPanel que se podrá visualizar dentro de otro. Una vez generado el código correspondiente, podremos entrar a editar el diseño visualmente, con lo que nos ahorraremos un considerable esfuerzo.
En éste post, espero dar un vistazo general al paquete de clases de Java de Swing, en concreto a la versión estándar 1.7 update 9. En la fecha en que escribo ésto, los Swing ya no están en desarrollo, con lo que no se van a añadir nuevas funcionalidades, ni componentes. En la web de Oracle, podemos ver la jerarquía completa del paquete javax.swing en la documentación original, aquí teneis el árbol de la jerarquía de clases:
Casi todas las clases que hay para crear nuestra interfaz de usuario van a empezar por J, como JFrame, JDialog, JButton, JLabel, etcétera… Ya tengo instalada la versión del JDK y del JRE más nueva ahora mismo para asegurarme de que tengo al día el paquete de clases. Así pues, la ayuda contextual de Eclipse me muestra un listado de todos éstos componentes:
Tenemos 46 clases en éste listado, de las cuales la clase con la que se contruyen todas las demás clases visuales es JComponent. El resto de clases de Swing que no comienzan por J, ayudan o nos sirven de herramientas para éstas. Quitando la JComponent, nos quedan 45 clases que «dibujan» nuestra interfaz gráfica.
Estado actual
Swing se contruyó a partir de AWT, es decir se basa en AWT, que es más dependiente del Sistema Operativo. De ésta forma los programas hechos con Swing son más independientes del Sistema Operativo, así que mejor si usamos Swing porque está más elaborado. Otro tema es JavaFX, que es el siguiente «level» en la programación gráfica que la gente de Java están desarrollando. JavaFX promete ser más independiente del Sistema Operativo si aún cabe, prometiendo en un futuro que tu programa desarrollado en JavaFX llegue a ser compatible con cualquier dispositivo que tenga gráficos, como móviles, televisiones, coches, incluso en una página web dicen en los vídeos de Oracle siempre que tengamos la máquina virtual de Java instalada. Pero todo ésto se va del tema del post que tenemos entre manos…
Contenedores externos
Volviendo a los Swing, de todas éstas clases tenemos las clases que nos van a ayudar a estructurar nuestro programa en ventanas, diálogos, paneles.. en donde vamos a poner el resto de componentes. Éstas clases son las siguientes:
JFrame -> es la madre del cordero, la clase que normalmente se usa para que contenga el resto de componentes, es la principal.
JApplet -> para construir un applet.
JDesktopPane -> crea una zona donde podemos poner ventanas internas (JInternalFrame).
JDialog -> es una ventana de diálogo.
JInternalFrame -> ventana hija que debe estar dentro de un escritorio (JDesktopPane).
JLayer -> crea un borde alrededor de otro componente.
JLayeredPane -> es un panel con borde.
JPanel -> un panel simple sin adornos ni nada.
JRootPane -> panel principal que puede contener otros objetos.
JScrollPane -> es un panel que sirve para contener objetos que se salen del campo, entonces con barras de scroll podemos movernos por ellos.
JSplitPane -> sirve para dividir una zona en dos, en cada subdivisión podemos poner otros elementos.
JTabbedPane -> parecido al JSplitPane pero divide las zonas en pestañas.
JViewPort -> es un objeto que sirve para visualizar los otros objetos que se mueven en una ventana, como por ejemplo con barras de scroll.
JWindow -> es como un JFrame pero sin bordes ni botones para cerrar, maximizar y minimizar.
Pueden crearso objetos anteriores y meterse unos dentro de otros, algunos se pueden omitir y otros son necesarios. Por ejemplo si queremos ventanas internas debemos de ponerlas dentro de un panel de escritorio (JDesktopPane), sino no funcionará.
El resto de componentes
Vistos los anterioes 14 elementos, nos quedan 45 menos 14 que son 31 componentes. Éstos que quedan son más conocidos y en cuanto leemos sobre ellos seguro que sabemos de qué hablamos.
Los clásicos:
JButton -> para hacer botones.
JCheckBox -> cajas chequeables.
JComboBox -> pues el combo de siempre, creo que se llama «caja de selección desplegable».
JLabel -> para poner etiquetas.
JList -> implementa una lista seleccionable.
JOptionPane -> panel donde podemos poner opciones a elegir.
JPasswordField -> campo de contraseña.
JProgressBar -> barras de progreso.
JRadioButton -> estos son los elementos que dan varias opciones a elegir, en la que sólo una es posible.
JEditorPane -> para poner textos complejos, admite HTML y texto en el formato Rich Text Format (RTF).
JFormattedTextField -> para poner textos con formato.
JTextArea -> areas de texto donde podemos poner textos grandes, multilínea.
JTextField -> areas de textos pequeños, de una sola linea de texto.
JTextPane -> admite textos formateados, iconos y componentes.
Unas utilidades:
JColorChooser -> nos sirve para mostrar una ventana donde podemos elegir un color.
JFileChooser -> éste objeto es una ventana para elegir un archivo.
Menús:
JMenu -> ésto es un menú que podemos poner dentro de una barra de menú o dentro de otro menú.
JMenuBar -> barra de menús, dentro de ésta se ponen el resto de los elementos de los menús.
JMenuItem -> es un elemento de menú.
JCheckBoxMenuItem -> cajas de texto seleccionable en un menú.
JPopupMenu -> implementa un menú desplegable.
JRadioButtonMenuItem -> para poner opciones a elegir en un menú.
JSeparator -> es una raya entre elemento y elemento de menú.
Otros:
JToolBar -> es una barra de herramientas para personalizar.
JToolTip -> muestra un texto al pasar por encima de un componente.
JScrollBar -> barras de scroll.
JSlider -> es una barra que puedes mover arriba y abajo con la que elijes un valor.
JSpinner -> es una caja de texto con dos botones arriba y abajo para cambiar el valor de la caja.
JToggleButton -> botón con dos estados, presionado y no presionado.
JTable -> se usa para mostrar tablas de datos, como su nombre indica.
JTree -> es un árbol, que puede servir para mostrar por ejemplo un árbol de directorios.
Con ésto ya nos podemos hacer una idea de las posibilidades que tenemos para hacer una interfaz de usuario con Swing.
Aquí dejo cómo crear una primera aplicación gráfica en Java usando Swing y Eclipse. El ejercicio clásico ¡Hola Mundo! usando una tecnología que nos brinda Java crear aplicaciones con entorno gráfico, Java Swing. Y para verlo, el entorno de desarrollo elegido ha sido Eclipse.
Curioseando qué tal van los desarrollos, con respecto a las interfaces gráficas de usuario (llamadas GUI para los amigos), veo que tengo otra nueva actualización del Window Builder. El Window Builder es un conjunto de plugins de Eclipse que nos van a permitir hacer GUIs en Java sin gastar casi tiempo.
A fecha de hoy tengo la versión Juno de Eclipse, y me acabo de instalar el plugin Swing Designer que está disponible dentro del propio Eclipse, en la famosa opción Help -> Install New Software. Instalada la versión 1.5.1 del Swing Designer, me he puesto manos a la obra.
En el Explorador de Proyectos, donde estés creando tu aplicación Java, le das con el botón derecho del ratón a New -> Other… Y verás una ventana como la siguiente donde puedes elegir todo el tipo de ficheros, clases, o lo que sea que quieras hacer:
Se parece a Visual Studio, NetBeans o IDEs similares ¿verdad? Personalmente no creo que Eclipse tenga nada que envidiar a ningún otro entorno de desarrollo de pago, o no de pago. ¿Qué más queremos teniendo Eclipse? Parezco un vendedor de Eclipse (jejejje es broma). Bromas aparte, depende de dónde te encuentres te irá mejor un entorno de desarrollo u otro…
Seguimos con lo que tenemos entre manos. He creado mi nueva Application Window que he llamado HolaMundoSwing y se me abre una ventana dentro de Eclipse tal que así:
Me ha generado 44 líneas de código, con las que ya tengo mi ventana con el programa básico. Tenemos disponible absolutamente todo el código necesario para crear aplicaciones con GUIs, es decir, que no se nos esconde nada del código fuente. Podríamos usar éste mismo código fuente en línea de comandos, y compilar nuestro programa Java con el javac.exe sin usar Eclipse. Lo que quiero explicar, es que Eclipse te genera y te da todo el código fuente, sin crear archivos secundarios en tu proyecto donde gestione la parte gráfica. No te atas a Eclipse. Es decir, en éste caso todo lo necesario está dentro del mismo fichero, te puedes llevar ésta recién creada aplicación de ventanas a otro sitio con sólo llevarte el fichero HolaMundoSwing.java y seguir programando.
Vamos ahora a lo que más nos va a interesar, el diseñador de formularios que nos va a ahorrar mucho tiempo en el diseño de la interfaz de usuario. Abajo tenemos una pestaña que pone ‘Design’, diseño en castellano. Le damos y nos sale una ventana como la siguiente. Aquí es donde podemos editar visualmente todo tipo de componentes visuales del estilo de botones, cajas de texto, grupos de selección, podemos poner imágenes, menús, y un largo etcétera…
Cómo se usa cada componente merece un post, así que éste lo voy a dejar así.
Lo lógico es que quieras juguetear poniendo componentes en tu nueva ventana. Para poder posicionarlos donde quieras hay que poner antes de nada en el formulario un ‘Absolute layer’ que tenemos en la sección ‘Layouts’. Después ya podrás poner los componentes donde quieras. En pocos segundos he puesto un botón, una etiqueta y una caja de selección, como podemos ver en la imagen de aquí arriba. Si volvemos a la pestaña de abajo donde pone ‘Source’, en nuestro Eclipse, veremos que automáticamente se ha generado todo el código correspondiente para dibujar los componentes en nuestro GUI. Queda después añadir el código para hacer funcionar cada cosa.
Puedes hacer doble click en el botón para ver la facilidad con que puedes empezar a programar. Se te pondrá delante el código fuente y la función donde puedes programar el click del botón. Verás un código tal que así:
JButton btnNewButton = new JButton(«Botu00F3n»); btnNewButton.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { // AL HACER CLICK EJECUTA ÉSTO } }); btnNewButton.setBounds(10, 11, 89, 23); frmholaMundoSwing.getContentPane().add(btnNewButton);
Aquí queda ésta pequeña introducción a los Swings de Java. Si quieres hacer un fichero ejecutable de Java, un .JAR y distribuir el programa que acabas de hacer; en la ventana del explorador de proyectos, le das con el botón derecho del ratón al fichero HolaMundoSwing.java. Se despliega el menú y le haces click en Exportar. Luego eliges Java JAR file. Eliges destino. Y ya tienes un ejecutable .jar que puedes usar en cualquier ordenador con el JRE instalado.