Lamento que andes corto de tiempo. Voy a intentar avanzar y explicar lo que pueda, pero por desgracia mi tiempo libre es muy escaso.
Lo siguiente que quería hacer era poder registrar nuevos Usuarios, que según el enunciado serán Pasajeros necesariamente. Parece ser que no hay opción de registrar administradores, para añadir más admin a parte del que puse en el código, sería escribiendo sus datos directamente en el txt.
Bueno, la forma de hacer un registro es desde la pantalla de login, se introduce cédula y password, y se marca el checkbox para indicar que queremos hacer nuevo registro:
![](https://i.ibb.co/HCW2fSV/imagen-2021-04-18-002834.png)
Al pulsar el botón, ha de aparecer un formulario mostrando la cédula y password introducidos, además de otros campos como edad, email, radiobutton para el genero, un selector de fecha de nacimiento(JDateChooser) y un campo edad (no editable) donde la edad se autocalcula según la fecha seleccionada.
Así que toca escribir una nueva clase JPanel para modelar este formulario.
Este es el código que he escrito:
package gui;
import java.awt.BorderLayout;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Date;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JPasswordField;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
import com.toedter.calendar.JDateChooser;
@SuppressWarnings("serial")
public class PanelFormularioPasajero extends JPanel {
private JTextField jtCedula;
private JPasswordField jtPassword;
private JTextField jtNombre;
private JTextField jtEdad;
private JTextField jtEmail;
private JRadioButton rbHombre;
private JRadioButton rbMujer;
private JDateChooser selecFecha;
private JButton btRegistrar;
private JButton btCancelar;
public PanelFormularioPasajero() {
jtCedula = new JTextField(8);
jtPassword = new JPasswordField(8);
jtNombre = new JTextField(8);
jtEdad = new JTextField(4);
jtEdad.setEditable(false);
jtEmail = new JTextField(8);
rbHombre = new JRadioButton("Hombre");
rbHombre.setSelected(true);
rbMujer = new JRadioButton("Mujer");
ButtonGroup gb = new ButtonGroup();
gb.add(rbHombre); gb.add(rbMujer);
selecFecha = new JDateChooser("dd/MM/yy", "##/##/##", '-');
selecFecha.setDate(new Date()); //Comienza con fecha actual
selecFecha.setMaxSelectableDate(new Date()); //Y Además no se permite fechas posteriores a hoy
btRegistrar = new JButton("Registrar");
btCancelar = new JButton("Cancelar");
btCancelar.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
resetFormulario();
}
});
setLayout(new BorderLayout());
add(new PanelNorte(), BorderLayout.NORTH);
add(new PanelCentro(), BorderLayout.CENTER);
add(new PanelSur(), BorderLayout.SOUTH);
}
private class Panel1 extends JPanel {
public Panel1() {
JPanel pnCedula = new JPanel();
pnCedula.add(new JLabel("Cédula:"));
pnCedula.add(jtCedula);
JPanel pnNombre = new JPanel();
pnNombre.add(new JLabel("Nombre:"));
pnNombre.add(jtNombre);
JPanel pnPassword = new JPanel();
pnPassword.add(new JLabel("Contraseña:"));
pnPassword.add(jtPassword);
setLayout(new GridLayout(1,3));
add(pnCedula); add(pnNombre); add(pnPassword);
}
}
private class Panel2 extends JPanel {
public Panel2() {
JPanel pnEmail = new JPanel();
pnEmail.add(new JLabel("E-Mail:"));
pnEmail.add(jtEmail);
JPanel pnEdad = new JPanel();
pnEdad.add(new JLabel("Edad:"));
pnEdad.add(jtEdad);
JPanel pnGenero = new JPanel();
pnGenero.add(rbHombre);
pnGenero.add(rbMujer);
setLayout(new GridLayout(1,3));
add(pnEmail); add(pnEdad); add(pnGenero);
}
}
private class Panel3 extends JPanel {
public Panel3() {
add(new JLabel("Fecha Nacimiento:"));
add(selecFecha);
}
}
private class PanelNorte extends JPanel {
public PanelNorte() {
JLabel titulo = new JLabel("Registro de nuevo usuario");
titulo.setFont(new Font("Verdana", Font.ITALIC, 36));
add(titulo);
setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createEmptyBorder(20, 20, 0, 20),
BorderFactory.createRaisedSoftBevelBorder()));
}
}
private class PanelCentro extends JPanel {
public PanelCentro() {
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
add(new Panel1()); add(new Panel2()); add(new Panel3());
setBorder(BorderFactory.createEmptyBorder(20, 20, 10, 20));
}
}
private class PanelSur extends JPanel {
public PanelSur() {
JPanel pnRegistrar = new JPanel();
pnRegistrar.add(btRegistrar);
JPanel pnCancelar = new JPanel();
pnCancelar.add(btCancelar);
setLayout(new BoxLayout(this, BoxLayout.X_AXIS));
add(pnRegistrar);
add(pnCancelar);
setBorder(BorderFactory.createEmptyBorder(10, 20, 20, 20));
}
}
//Metodos
/**
* Junta todos los datos en un array y los retorna.<br>
* Puesto que hay datos de distintas clases, String y Date,
* los retornamos como Object.
* @return Array de Object con los datos del formulario.
*/
public Object[] getDatos() {
Object[] datos = new Object[6];
datos[0] = jtCedula.getText();
datos[1] = String.valueOf(jtPassword.getPassword());
datos[2] = jtNombre.getText();
datos[3] = jtEmail.getText();
datos[4] = rbHombre.isSelected()?"Hombre":"Mujer";
datos[5] = selecFecha.getDate();
//La edad no se retorna, pero se calcula para mostrar en formulario
calcularEdad();
return datos;
}
/**
* Borra todos los datos insertados dejando limpio
* el formulario.
*/
public void resetFormulario() {
jtCedula.setText(null);
jtPassword.setText(null);
jtNombre.setText(null);
jtEdad.setText(null);
jtEmail.setText(null);
selecFecha.setDate(new Date());
}
/**
* Setea un valor para la cédula.<br>
* Este valor provendrá del formulario de login, cuando el usuario
* ha solicitado registrarse como nuevo usuario.
* @param cedula Valor String para el campo cédula
*/
public void setCedula(String cedula) {
jtCedula.setText(cedula);
}
/**
* Setea un valor para la contraseña.<br>
* Este valor provendrá del formulario de login, cuando el usuario
* ha solicitado registrarse como nuevo usuario.
* @param pass Valor String para el campo contraseña.
*/
public void setPassword(String pass) {
jtPassword.setText(pass);
}
/**
* Calcula la edad del usuario recuperando la fecha seleccionada en el
* JDateChooser y comparando con la fecha actual.<br>
*/
private void calcularEdad() {
Date selec = selecFecha.getDate();
Date hoy = new Date();
long diferencia = hoy.getTime() - selec.getTime();
//Convertimos a horas
long horas = diferencia/3600000;
//Horas a dias
int dias = (int) (horas/24);
//Y los años serán la edad
int edad = dias/365;
jtEdad.setText(Integer.toString(edad));
}
/**
* Una validación muy simple para comprobar si el valor introducido
* en el campo Email tiene el formato esperado para una dirección de
* correo electrónico.<br>
* La comprobación es muy básica, se limita a ver si hay una arroba y al menos un punto.
* Y que este punto está después de la arroba, con al menos 1 carácter entre ambos.
* @return TRUE si parece un e-mail válido en su formato, FALSE en caso contrario.
*/
public boolean validarEmail() {
String email = jtEmail.getText();
if (email.isBlank())
return false;
else {
int arroba = email.indexOf('@');
int punto = email.lastIndexOf('.');
if (arroba == -1 || punto == -1)
return false;
else
return punto - arroba > 1;
}
}
/*
* Los siguientes métodos son para agregar acciones a los botones
* del formulario. Estas acciones se escriben desde la clase Main
* donde se puede interactuar con otros objetos como paneles y el
* gestor de usuarios, que no son visibles desde esta clase.
*
* El botón Cancelar recibe una segunda acción desde esta misma clase,
* escrita en el constructor, que es simplemente resetear el formulario.
*/
public void setAccionRegistrar(ActionListener accion) {
btRegistrar.addActionListener(accion);
}
public void setAccionCancelar(ActionListener accion) {
btCancelar.addActionListener(accion);
}
}
De él quiero comentar cosas sobre el JDateChooser. Nunca antes lo había usado, ya que es una librería externa "no oficial". Es muy útil sin duda, aunque como ya comenté, está basado en clases casi en desuso como Date y Calendar.
Revisando su documentación, he visto que en su constructor podemos indicarle que formato queremos para mostrar la fecha, así como una "careta" para que el campo de texto que incluye tome el tamaño adecuado.
El tercer argumento, el guión, es para indicar que caracter queremo usar como separador entre
dia-mes-añoselecFecha = new JDateChooser("dd/MM/yy", "##/##/##", '-');
Para que el campo de texto del selector no aparezca vacío, le seteo la fecha actual:
selecFecha.setDate(new Date()); //Comienza con fecha actual
El enunciado no dice nada sobre límites de edad para registrar pasajeros, así que en principio se puede registrar alguien nacido hoy mismo, por lo que tendría 0 años....
Sin embargo, si he creído oportuno impedir que se puedan seleccionar fechas posteriores a la actual y esto lo limito con este método:
selecFecha.setMaxSelectableDate(new Date());
Explicado esto, veamos los métodos que va a tener esta clase.
El primero será para recopilar todos los datos del formulario y retornarlos. La idea es recibirlos en la clase Main, desde donde tomaremos decisiones según estos datos.
public Object[] getDatos() {
Object[] datos = new Object[6];
datos[0] = jtCedula.getText();
datos[1] = String.valueOf(jtPassword.getPassword());
datos[2] = jtNombre.getText();
datos[3] = jtEmail.getText();
datos[4] = rbHombre.isSelected()?"Hombre":"Mujer";
datos[5] = selecFecha.getDate();
//La edad no se retorna, pero se calcula para mostrar en formulario
calcularEdad();
return datos;
}
Los datos los juntamos en un array para que vayan todos juntos. Si todos los valores fuesen String, podríamos usar un array de String. Pero hay un dato que no lo es, la fecha de nacimiento (es un Date), así que para que puedan ir todos juntos, usaremos un array de Object.
Esto luego veremos que supone un pequeño inconveniente, para operar con ellos habrá que casting a String y Date según corresponda, pero se compensa por la comodidad de poder enviarlos todos juntos en el mismo array.
Otro método será el típico de resetear formulario, no requiere explicaciones:
public void resetFormulario() {
jtCedula.setText(null);
jtPassword.setText(null);
jtNombre.setText(null);
jtEdad.setText(null);
jtEmail.setText(null);
selecFecha.setDate(new Date());
}
Tendrá dos setter, uno para setear la Cédula y otro para setear el Password. Esto es porque dichos datos serán recuperados desde el formulario de login inicial. Luego el usuario podrá modificarlos si quiere antes de registrar
public void setCedula(String cedula) {
jtCedula.setText(cedula);
}
public void setPassword(String pass) {
jtPassword.setText(pass);
}
Método interesante es el de calcular la edad. Se recupera la fecha seleccionada en el JDateChooser y la actual.
Se calcula la diferencia entre ambas, en milisegundos.... los cuáles para transformar en años, de forma que se entienda lo que se hace, pues primero convierto a horas, luego a días y luego a años.
De este modo, se obtiene la edad del Pasajero. Este dato no es un atributo de la clase Pasajero, pero el enunciado pide que se calcule y se muestre en un campo de texto.
private void calcularEdad() {
Date selec = selecFecha.getDate();
Date hoy = new Date();
long diferencia = hoy.getTime() - selec.getTime();
//Convertimos a horas
long horas = diferencia/3600000;
//Horas a dias
int dias = (int) (horas/24);
//Y los años serán la edad
int edad = dias/365;
jtEdad.setText(Integer.toString(edad));
}
También se pide que el campo de e-mail sea validado. Para ello uso este método, muy sencillo aunque efectivo, en el que lo que hago es comprobar si el valor introducido tiene una arroba y si dentro de la cadena está en una posición anterior al del último "punto" de la cadena.
De este modo, no aceptaría un email como este: correo.mail@es.
Pero sí uno como este:correo@mail.es
public boolean validarEmail() {
String email = jtEmail.getText();
if (email.isBlank())
return false;
else {
int arroba = email.indexOf('@');
int punto = email.lastIndexOf('.');
if (arroba == -1 || punto == -1)
return false;
else
return punto - arroba > 1;
}
}
Luego hay dos métodos para setear acciones a los dos botones del formulario, uno para registrar los datos y otro para cancelar y volver al formulario de login.
Esta clase nos mostrará una interfaz como esta:
![](https://i.ibb.co/7SBk1DR/imagen-2021-04-18-005807.png)
Y cuando pulsemos el botón "registrar", cuya acción luego escribiremos en la clase Main, haremos que nos pida confirmación de los datos introducidos.
![](https://i.ibb.co/h7KwsMR/imagen-2021-04-18-010048.png)
Y si confirmamos, se guardarán los datos en el archivo de texto, este nuevo usuario quedará logueado y se nos mostrará la pantalla del "Menú de Pasajeros"
![](https://i.ibb.co/J3zQVNB/imagen-2021-04-18-010455.png)
Este menú, según el enunciado, solo tendrá esas dos opciones.
Voy a poner el código para crear ese panel, es prácticamente calcado al que usamos para escribir el "Panel de Administrador", solo que en lugar de 4 botones, tendremos solo 2.
Al igual que el de Administrador, de momento estos botones no van a tener acciones.
package gui;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
@SuppressWarnings("serial")
public class PanelPasajero extends JPanel {
private BotonOpcion btBuscaRuta;
private BotonOpcion btCompraBoletos;
private JButton btLogout;
public PanelPasajero() {
btBuscaRuta = new BotonOpcion("Buscador<br>de<br>Rutas");
btCompraBoletos = new BotonOpcion("Comprar<br>Boletos");
btLogout = new JButton("Cerrar Sesión");
setLayout(new BorderLayout());
add(new PanelNorte(), BorderLayout.NORTH);
add(new PanelCentral(), BorderLayout.CENTER);
add(new PanelSur(), BorderLayout.SOUTH);
}
private class PanelNorte extends JPanel {
public PanelNorte() {
JLabel titulo = new JLabel("Menú de Pasajeros");
titulo.setFont(new Font("Verdana", Font.ITALIC, 36));
add(titulo);
setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createEmptyBorder(20, 20, 0, 20),
BorderFactory.createRaisedSoftBevelBorder()));
}
}
private class PanelCentral extends JPanel {
public PanelCentral() {
setLayout(new GridLayout(1,1,15,15));
add(btBuscaRuta); add(btCompraBoletos);
setBorder(BorderFactory.createEmptyBorder(20, 20, 20, 20));
}
}
private class PanelSur extends JPanel {
public PanelSur() {
JPanel pnBoton = new JPanel();
pnBoton.add(btLogout);
add(pnBoton);
}
}
private class BotonOpcion extends JButton {
public BotonOpcion(String texto) {
super("<html><p style=\"text-align:center\">" + texto + "</p></html>");
setFont(new Font("Verdana", Font.BOLD, 18));
setFocusPainted(false);
setBackground(new Color(102, 204, 255));
setForeground(Color.WHITE);
setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createEtchedBorder(),
BorderFactory.createEmptyBorder(10, 10, 10, 10)));
addMouseListener(new MouseListener() {
@Override
public void mouseClicked(MouseEvent e) {}
@Override
public void mousePressed(MouseEvent e) {}
@Override
public void mouseReleased(MouseEvent e) {}
@Override
public void mouseEntered(MouseEvent e) {
setForeground(new Color(204, 0, 0));
}
@Override
public void mouseExited(MouseEvent e) {
setForeground(Color.WHITE);
}
});
}
}
//Métodos
public void setAccionLogout(ActionListener accion) {
btLogout.addActionListener(accion);
}
}
Bien, tenemos escrito el código para modelar el panel registro de nuevos usuarios y el del menú de opciones del pasajero.
En el siguiente post voy a poner los cambios que hay que hacer en la clase Main para incluirlos en nuestro programa.