Programación Avanzada 2.º BT TI · DGETP / UTU
Profe Macón
Explora, aprende, crea.
profemacon.net

Eventos y actualización del listado en ProductoVista

La ventana ya está construida. Ahora vamos a conectar sus botones, mantener provisionalmente los productos en un ArrayList dentro de Main, limpiar el formulario y lograr que la JTable refleje los cambios.

1 · Repartir responsabilidades

Main coordina; ProductoVista sabe mostrarse

ProductoVista

  • Crea y organiza componentes.
  • Permite leer los campos.
  • Sabe limpiar sus propios campos.
  • Sabe convertir una lista de productos en filas de la tabla.
  • Permite registrar listeners.

Main (por ahora)

  • Crea la vista.
  • Mantiene el ArrayList<Producto>.
  • Decide qué hacer ante cada evento.
  • Agrega y elimina productos.
  • Decide cuándo refrescar y cuándo limpiar.
Regla: Main decide cuándo hay que limpiar; ProductoVista sabe cómo se limpian sus componentes.
Supuesto actual: para concentrarnos en eventos y actualización, suponemos por ahora que los datos ingresados son válidos. La validación vendrá después.
2 · Preparar ProductoVista

La Vista necesita una pequeña API pública

El modelo de la tabla pasa a ser atributo

private JTable tablaProductos;
private DefaultTableModel modeloTabla;

Leer datos sin exponer los JTextField

public String getCodigo() {
    return txtCodigo.getText();
}

public String getNombre() {
    return txtNombre.getText();
}

public String getPrecio() {
    return txtPrecio.getText();
}

public String getStock() {
    return txtStock.getText();
}

Limpiar el formulario

public void limpiarCampos() {
    txtCodigo.setText("");
    txtNombre.setText("");
    txtPrecio.setText("");
    txtStock.setText("");

    txtCodigo.requestFocusInWindow();
}

Registrar listeners

public void listenerAceptar(ActionListener listener) {
    btnAceptar.addActionListener(listener);
}

public void listenerEliminar(ActionListener listener) {
    btnEliminar.addActionListener(listener);
}

public void listenerCancelar(ActionListener listener) {
    btnCancelar.addActionListener(listener);
}
Nombres: usamos listenerAceptar, listenerEliminar y listenerCancelar. lsnAceptar también funcionaría, pero la versión completa es más clara para aprender.
3 · Primer evento

Cancelar: comprobar el circuito más simple

vista.listenerCancelar(e -> {
    vista.limpiarCampos();
});
clic en Cancelar
↓
ActionEvent
↓
vista.limpiarCampos()

Main no conoce txtCodigo, txtNombre, etc. Solo le pide a la Vista que se limpie.

4 · Incorporar datos

El ArrayList vive en Main, por ahora

ArrayList<Producto> productos = new ArrayList<>();

ProductoVista vista = new ProductoVista();

El ArrayList será la fuente de verdad provisional. La tabla no contiene “los productos reales”: solo los representa.

Acción de Aceptar

vista.listenerAceptar(e -> {

    String codigo = vista.getCodigo().trim();
    String nombre = vista.getNombre().trim();

    double precio =
            Double.parseDouble(vista.getPrecio());

    int stock =
            Integer.parseInt(vista.getStock());

    Producto producto =
            new Producto(codigo, nombre, precio, stock);

    productos.add(producto);

    vista.actualizarTabla(productos);

    vista.limpiarCampos();
});
leer campos → crear Producto → ArrayList.add() → actualizarTabla() → limpiarCampos()
5 · Actualizar la JTable

ProductoVista sabe transformar Productos en filas

public void actualizarTabla(List<Producto> productos) {

    modeloTabla.setRowCount(0);

    for (Producto producto : productos) {

        Object[] fila = {
            producto.getCodigo(),
            producto.getNombre(),
            producto.getPrecio(),
            producto.getStock()
        };

        modeloTabla.addRow(fila);
    }
}
ArrayList<Producto>
↓ llamada manual
actualizarTabla()
↓
DefaultTableModel
↓ evento del modelo
JTable se actualiza visualmente
Importante: el ArrayList no avisa a la tabla. Nosotros llamamos a actualizarTabla(productos). Lo automático ocurre entre DefaultTableModel y JTable.
Relación con Observer

La llamada desde Main a actualizarTabla() es explícita, así que todavía no estamos usando Observer para nuestra colección. Sin embargo, la relación entre TableModel y JTable sí utiliza un mecanismo de notificación por eventos relacionado con ese patrón.

6 · Eliminar

La Vista informa qué fila seleccionó el usuario

public int getFilaSeleccionada() {
    return tablaProductos.getSelectedRow();
}
vista.listenerEliminar(e -> {

    int fila = vista.getFilaSeleccionada();

    if (fila >= 0) {
        productos.remove(fila);

        vista.actualizarTabla(productos);

        vista.limpiarCampos();
    }
});
Limitación consciente: esta versión supone que el orden de la JTable coincide con el del ArrayList. Cuando agreguemos filtrado u ordenamiento, será mejor identificar el producto por su código y no por el número de fila.
7 · Integración

Main completo en esta etapa

import java.util.ArrayList;

public class Main {

    public static void main(String[] args) {

        ArrayList<Producto> productos =
                new ArrayList<>();

        ProductoVista vista =
                new ProductoVista();


        vista.listenerCancelar(e -> {
            vista.limpiarCampos();
        });


        vista.listenerAceptar(e -> {

            String codigo = vista.getCodigo().trim();
            String nombre = vista.getNombre().trim();

            double precio =
                    Double.parseDouble(vista.getPrecio());

            int stock =
                    Integer.parseInt(vista.getStock());

            Producto producto =
                    new Producto(
                        codigo,
                        nombre,
                        precio,
                        stock
                    );

            productos.add(producto);

            vista.actualizarTabla(productos);
            vista.limpiarCampos();
        });


        vista.listenerEliminar(e -> {

            int fila = vista.getFilaSeleccionada();

            if (fila >= 0) {
                productos.remove(fila);

                vista.actualizarTabla(productos);
                vista.limpiarCampos();
            }
        });


        vista.actualizarTabla(productos);
        vista.setVisible(true);
    }
}
SituaciónMain decideProductoVista hace
CancelarHay que limpiarLimpia sus campos
AceptarCrea y agrega ProductoEntrega datos, muestra lista y limpia
EliminarQuita del ArrayListEntrega fila, muestra lista y limpia
RefrescarDecide cuándoTransforma productos en filas
8 · Qué empieza a pasar

Main está creciendo… y eso es intencional

Ahora podemos ver el circuito completo sin esconderlo detrás de una arquitectura nueva:

evento → Main decide → cambia ArrayList → Vista se actualiza

Cuando este circuito esté claro, el siguiente problema aparecerá solo: Main estará acumulando demasiadas responsabilidades. Ahí tendrá sentido introducir ProductoController y trasladar allí la coordinación que hoy está en Main.

Material en curso