La interfaz podría ser algo así:
Una imagen de encabezado (podría ser un logo de un hospital o cualquier otra cosa). Un panel de datos del caso con nombre, apellidos, género, edad, estado y prueba covid confirmado sí/no. A su vez, botones "Nuevo registro", "Modificar", "Eliminar", "Guardar", "Grabar" y "Salir". Una tabla de datos de pacientes con nombre, apellidos, género, edad, confirmado, estado (recuperado/activo/fallecido...).
La ventana está dividida en tres partes.
Arriba simplemente se muestra la imagen de Covid, para darle un poquito más de vistosidad a la aplicación.
Debajo hay un formulario para insertar y modificar los datos, junto con botones para las distintas funciones.
Abajo de todo, una tabla (no editable, para simplificar el código) en la que se muestran los casos registrados.
Al pinchar en uno de esos casos, los datos se muestran en los campos del formulario.
Si se pulsa el botón "Modificar" entonces se permite modificar los valores en el formulario y con el botón "Guardar" se aceptan los cambios.
Sin embargo, este botón solo guarda los cambios en memoria (en el ArrayList). Si se quiere hacerlos persistentes hay que pulsar el botón "Grabar" para que se guarden los cambios en disco.
Bien, estas tres partes de la ventana, están creadas con tres clases distintas que heredan de JPanel.
La primera clase,
PanelImagen, es muy sencilla, se encarga simplemente de "pintar" un panel con la imagen que le indiquemos por su constructor:
package gui;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
public class PanelImagen extends JPanel{
private Image imagen;
public PanelImagen(String rutaImagen) {
imagen = new ImageIcon(getClass().getClassLoader().getResource(rutaImagen)).getImage();
setPreferredSize(new Dimension(599, 158));
}
@Override
public void paint(Graphics g){
g.drawImage(imagen, 0, 0, 599, 158, this);
setOpaque(false);
super.paint(g);
}
}
La segunda,
PanelDatos, muestra el formulario y los botones de funciones.
Está a su vez dividida en dos subpaneles, uno con los campos de texto, combobox, el checkbox, etc ..
Y otro con la línea de botones.
Algunos botones, sus acciones (ActionListener) están escritos en esta misma clase porque son acciones sencillas que no necesitan interactuar con otras partes del código.
Por ejemplo el botón "SALIR" para cerrar la aplicación o el botón "Nuevo Registro" que lo único que hace es limpiar los campos y activarlos para que se pueda escribir en ellos.
Otros botones, sus acciones se escribirán desde otra parte del código, porque si tendrán que interactuar con el "gestor de casos" y/o con el panel que mostrará la tabla.
Hay que destacar que este panel tiene un objeto
Caso como atributo. Este atributo es una referencia al
Caso que consta como seleccionado en la tabla. Tener esta referencia facilita saber de quién son los datos que hay que mostrar y/o modificar en los campos.
package gui;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.JTextField;
import javax.swing.SpinnerNumberModel;
import casos.Caso;
public class PanelDatos extends JPanel{
//Referencia a caso seleccionado en tabla
private Caso casoSeleccionado = null;
private JTextField jtNombre;
private JTextField jtApellidos;
private JComboBox<String> comboGenero;
private JSpinner jsEdad;
private JCheckBox checkConfirmado;
private JComboBox<String> comboEstado;
private JButton btNuevo;
public JButton btModificar;
public JButton btEliminar;
public JButton btGuardar;
public JButton btGrabar;
private JButton btSalir;
public PanelDatos() {
iniciarComponentes();
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
add(new PanelRegistro());
add(new PanelBotones());
nuevoRegistro(); //Formulario inicia listo para crear un nuevo registro
}
private void iniciarComponentes() {
jtNombre = new JTextField(12);
jtApellidos = new JTextField(18);
comboGenero = new JComboBox<String>(new String[] {"Hombre", "Mujer"});
jsEdad = new JSpinner(new SpinnerNumberModel(18, 0, 99, 1));
checkConfirmado = new JCheckBox("Confirmado");
comboEstado = new JComboBox<String>(new String[] {"Activo", "Recuperado", "Fallecido"});
btNuevo = new JButton("<html><p style=\"text-align:center\">Nuevo<br>Registro</p></html>");
btNuevo.addActionListener(new AccionNuevo());
btModificar = new JButton("Modificar");
btModificar.addActionListener(new AccionModificar());
btEliminar = new JButton("Eliminar");
btGuardar = new JButton("Guardar");
btGrabar = new JButton("Grabar");
btSalir = new JButton("SALIR");
btSalir.addActionListener(new AccionSalir());
}
//Paneles
private class PanelRegistro extends JPanel {
public PanelRegistro() {
setLayout(new GridLayout(3,2,5,10));
add(new PanelConLabel("Nombre", jtNombre));
add(new PanelConLabel("Apellidos", jtApellidos));
add(new PanelConLabel("Genero", comboGenero));
add(new PanelConLabel("Edad", jsEdad));
add(new PanelConLabel("Estado", comboEstado));
add(new PanelConLabel("Prueba COVID", checkConfirmado));
setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createEmptyBorder(10, 10, 5, 10),
BorderFactory.createTitledBorder("Datos del Caso")));
}
}
private class PanelBotones extends JPanel {
public PanelBotones() {
add(btNuevo);
add(Box.createHorizontalStrut(20));
add(btModificar);
add(btEliminar);
add(btGuardar);
add(Box.createHorizontalStrut(20));
add(btGrabar);
add(btSalir);
setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createEmptyBorder(5, 10, 10, 10),
BorderFactory.createLoweredSoftBevelBorder()));
}
}
private class PanelConLabel extends JPanel {
public PanelConLabel(String textoLabel, JComponent componente) {
setLayout(new FlowLayout(FlowLayout.LEFT));
add(new JLabel(textoLabel));
add(componente);
}
}
//Acciones
private class AccionNuevo implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
nuevoRegistro();
}
}
private class AccionModificar implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
btModificar.setEnabled(false);
btGuardar.setEnabled(true);
btGrabar.setEnabled(false);
activarCampos(true);
}
}
private class AccionSalir implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
int resp = JOptionPane.showConfirmDialog(null, "¿Cerrar Programa?"
+ "\nSe perderán los datos no GRABADOS.", "Cerrar Programa", JOptionPane.YES_NO_OPTION);
if (resp == JOptionPane.YES_OPTION)
System.exit(0);
}
}
//Métodos
/**
* Activa o desactiva los campos del formulario.<br>
* Los campos solo se activan cuando se está haciendo un nuevo
* registro o cuando se va a modificar un Caso existente.
* @param activar True para activar, False para desactivar
*/
private void activarCampos(boolean activar) {
jtNombre.setEditable(activar);
jtApellidos.setEditable(activar);
comboGenero.setEnabled(activar);
jsEdad.setEnabled(activar);
checkConfirmado.setEnabled(activar);
comboEstado.setEnabled(activar);
}
/**
* Prepara el formulario para registrar un nuevo caso.
*/
public void nuevoRegistro() {
casoSeleccionado = null; //Anulamos cualquier referencia a un caso ya existente
jtNombre.setText(null);
jtApellidos.setText(null);
comboGenero.setSelectedIndex(0);
jsEdad.setValue(18);
checkConfirmado.setSelected(false);
comboEstado.setSelectedIndex(0);
activarCampos(true);
btModificar.setEnabled(false);
btEliminar.setEnabled(false);
btGuardar.setEnabled(true);
btGrabar.setEnabled(false);
}
/**
* Comprueba que el formulario tenga los datos necesarios
* para crear un Caso y lo retorna.<br>Este método se invoca
* cuando se pulsa el botón "Guardar" de la interfaz.
* @return Un Caso con los datos del formulario
*/
public Caso getCaso() {
//Construimos un Caso con los datos del formulario
String nombre = jtNombre.getText();
if (nombre.isBlank()) {
JOptionPane.showMessageDialog(null, "Campo NOMBRE no puede estar vacío",
"Guardar Caso", JOptionPane.WARNING_MESSAGE);
return null;
}
String apellidos = jtApellidos.getText();
if (apellidos.isBlank()) {
JOptionPane.showMessageDialog(null, "Campo APELLIDOS no puede estar vacío",
"Guardar Caso", JOptionPane.WARNING_MESSAGE);
return null;
}
String genero = (String) comboGenero.getSelectedItem();
int edad = (int) jsEdad.getValue();
boolean confirmado = checkConfirmado.isSelected();
String estado = (String) comboEstado.getSelectedItem();
Caso caso = new Caso(nombre, apellidos, genero, edad, confirmado, estado);
//Antes de retornar el Caso, cambiamos configuración de botones interfaz
btModificar.setEnabled(true);
btEliminar.setEnabled(true);
btGuardar.setEnabled(false);
btGrabar.setEnabled(true);
activarCampos(false);
return caso;
}
/**
* Indica si estamos modificando un caso existente
* o creando uno nuevo.
* @return True si estamos modificando, False si es nuevo.
*/
public boolean esModificacion() {
//Si hay un caso seleccionado, es que SÍ estamos modificando.
return casoSeleccionado != null;
}
/**
* Recibe el Caso que ha de constar como seleccionado y por
* tanto muestra los datos del dicho Caso en el formulario.
* Si se recibe un valor null, es que en este momento
* no hay ningún caso seleccionado y los campos vuelven a su
* estado por defecto.<br>Este método se invoca cuando se pulsa
* en un registro de la tabla de casos.
* @param caso El Caso que se ha seleccionado.
*/
public void seleccionarCaso(Caso caso) {
casoSeleccionado = caso;
if (casoSeleccionado == null) {
jtNombre.setText(null);
jtApellidos.setText(null);
comboGenero.setSelectedIndex(0);
jsEdad.setValue(18);
checkConfirmado.setSelected(false);
comboEstado.setSelectedIndex(0);
}
else {
jtNombre.setText(casoSeleccionado.getNombre());
jtApellidos.setText(casoSeleccionado.getApellidos());
comboGenero.setSelectedItem(casoSeleccionado.getGenero());
jsEdad.setValue(casoSeleccionado.getEdad());
checkConfirmado.setSelected(casoSeleccionado.isConfirmado());
comboEstado.setSelectedItem(casoSeleccionado.getEstado());
}
activarCampos(false);
btModificar.setEnabled(true);
btEliminar.setEnabled(true);
btGuardar.setEnabled(false);
btGrabar.setEnabled(true);
}
}
La tercera clase,
PanelTabla, es solo un panel que muestra una tabla con los datos de los casos registrados.
Esta tabla se construye a partir de los datos del ArrayList que pusimos en la clase
GestionCasos, así que para facilitar esta comunicación recibirá una referencia a este ArrayList mediante el constructor de la clase.
Para simplificar el código, el modelo de la tabla está diseñado para que las celdas no se puedan editar.
La tabla solo mostrará los Casos y permitirá seleccionarlos. Pero para modificar los valores, habrá que usar los campos del formulario.
package gui;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ScrollPaneConstants;
import javax.swing.SwingConstants;
import javax.swing.border.BevelBorder;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.JTableHeader;
import casos.Caso;
public class PanelTabla extends JPanel{
private ArrayList<Caso> casos; //Referencia al ArrayList de GestionCasos
private MiTabla tablaCovid;
public PanelTabla(ArrayList<Caso> casosCovid) {
casos = casosCovid;
construirTabla();
JScrollPane sp = new JScrollPane(tablaCovid);
sp.setBorder(BorderFactory.createBevelBorder(BevelBorder.RAISED));
sp.setPreferredSize(new Dimension(630, 300));
sp.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
add(sp);
setBorder(BorderFactory.createCompoundBorder(
BorderFactory.createEmptyBorder(10, 10, 10, 10),
BorderFactory.createRaisedSoftBevelBorder()));
}
private class MiTabla extends JTable {
public MiTabla(String[][] datos, String[] nombresColumna) {
super(new DefaultTableModel(datos, nombresColumna) {
@Override
public boolean isCellEditable(int row, int column) {
return false; //Las celdas de esta tabla NO serán editables
}
});
//Pedimos al render que centre los textos en sus celdas.
DefaultTableCellRenderer centerRenderer = new DefaultTableCellRenderer();
centerRenderer.setHorizontalAlignment(SwingConstants.CENTER);
setDefaultRenderer(Object.class, centerRenderer);
//Cambiamos alguna fuente y color para los datos de la tabla
setFont(new Font("Verdana", Font.PLAIN , 12));
setForeground(Color.BLUE);
//También cambiamos estilo de fuente a la cabecera
JTableHeader cabecera = getTableHeader();
cabecera.setFont(new Font("Verdana", Font.BOLD , 14));
setAutoResizeMode(AUTO_RESIZE_OFF);
getColumnModel().getColumn(0).setPreferredWidth(135);
getColumnModel().getColumn(1).setPreferredWidth(160);
getColumnModel().getColumn(2).setPreferredWidth(80);
getColumnModel().getColumn(3).setPreferredWidth(50);
getColumnModel().getColumn(4).setPreferredWidth(50);
getColumnModel().getColumn(5).setPreferredWidth(130);
}
}
private void construirTabla() {
final String[] CABECERA = new String[] {"NOMBRE", "APELLIDOS", "GENERO", "EDAD",
"CONF.", "ESTADO"};
//Matriz para datos de la tabla
String[][] datos = new String[casos.size()][6];
for (int fila = 0; fila < datos.length; fila++) {
datos[fila] = casos.get(fila).datosArray();
}
tablaCovid = new MiTabla(datos, CABECERA);
}
public void actualizarTabla() {
DefaultTableModel modelo = (DefaultTableModel) tablaCovid.getModel();
modelo.setRowCount(0);
for (Caso caso: casos)
modelo.addRow(caso.datosArray());
tablaCovid.setModel(modelo);
}
public int getSeleccionado() {
return tablaCovid.getSelectedRow();
}
/**
* Recibe el "listener" que ha aplicarse en esta tabla.<br>
* Este listener detectará que registro de la tabla ha sido seleccionado y
* está escrito en la clase Main, desde donde se puede
* interactuar con las clases necesarias.
* @param listener MouseListener para detectar que registro ha recibido un click de raton.
*/
public void setTablaListener(MouseListener listener) {
tablaCovid.addMouseListener(listener);
}
}
Con estas tres clases, que son tres paneles, se construye el JFrame de la aplicación.
Este JFrame se crea en la clase
Main principal y lo vemos en el siguiente mensaje.