Saltearse al contenido

JTable en Java Swing

JTable es un componente de Swing que se utiliza para mostrar y editar datos como tablas. Proporciona una interfaz similar a una hoja de cálculo, permitiendo a los usuarios ver y manipular información en filas y columnas.

Uso Básico de JTable

Creación de una JTable básica

Para crear una JTable básica, necesitamos definir los datos y los nombres de las columnas:

1
import javax.swing.*;
2
import javax.swing.table.DefaultTableModel;
3
4
public class TablaBasica extends JFrame {
5
public TablaBasica() {
6
String[] columnNames = {"Nombre", "Edad", "Ciudad"};
7
Object[][] data = {
8
{"Juan", 25, "Salta"},
9
{"María", 30, "Corrientes"},
10
{"Carlos", 22, "San Juan"}
11
};
12
13
DefaultTableModel model = new DefaultTableModel(data, columnNames);
14
JTable table = new JTable(model);
15
16
JScrollPane scrollPane = new JScrollPane(table);
17
this.add(scrollPane);
18
19
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
20
this.pack();
21
this.setVisible(true);
22
}
23
24
public static void main(String[] args) {
25
SwingUtilities.invokeLater(() -> new TablaBasica());
26
}
27
}

Este ejemplo nos crea una tabla simple con tres columnas y tres filas. Utilizamos un DefaultTableModel para manejar los datos de la tabla.

Opciones Adicionales de JTable

Resaltar filas y columnas de la celda seleccionada

Para resaltar las filas y columnas de la celda seleccionada, podemos personalizar el renderizador de celdas:

1
import java.awt.Color;
2
import java.awt.Component;
3
import javax.swing.table.DefaultTableCellRenderer;
4
5
public class ResaltadoRenderer extends DefaultTableCellRenderer {
6
private int filaSeleccionada = -1;
7
private int columnaSeleccionada = -1;
8
9
@Override
10
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
11
Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
12
13
if (row == filaSeleccionada || column == columnaSeleccionada) {
14
c.setBackground(Color.LIGHT_GRAY); // establecemos el color de fondo
15
} else {
16
c.setBackground(table.getBackground()); // restauramos el color de fondo predeterminado
17
}
18
19
return c;
20
}
21
22
public void setSelectedCell(int row, int column) {
23
this.filaSeleccionada = row;
24
this.columnaSeleccionada = column;
25
}
26
}

Para usar este renderizador:

1
ResaltadoRenderer renderer = new ResaltadoRenderer();
2
table.setDefaultRenderer(Object.class, renderer);
3
4
table.getSelectionModel().addListSelectionListener(e -> {
5
if (!e.getValueIsAdjusting()) { // Si la selección ha terminado
6
int selectedRow = table.getSelectedRow();
7
int selectedColumn = table.getSelectedColumn();
8
renderer.setSelectedCell(selectedRow, selectedColumn);
9
table.repaint();
10
}
11
});

Crear una tabla editable con ComboBox y CheckBox

Para crear una tabla editable con diferentes tipos de editores para ciertas columnas, necesitamos personalizar el TableModel y los editores de celdas:

1
import javax.swing.table.DefaultTableModel;
2
3
public class ModeloTablaPersonalizado extends DefaultTableModel {
4
public ModeloTablaPersonalizado(Object[][] data, String[] columnNames) {
5
super(data, columnNames);
6
}
7
8
@Override
9
public Class<?> getColumnClass(int columnIndex) {
10
if (columnIndex == 3) { // Columna de CheckBox
11
return Boolean.class;
12
}
13
return super.getColumnClass(columnIndex);
14
}
15
16
@Override
17
public boolean isCellEditable(int row, int column) {
18
return true; // Todas las celdas son editables
19
}
20
}

Ahora, configuramos la tabla con este modelo y añadimos un ComboBox editor para una columna específica:

1
String[] columnNames = {"Nombre", "Edad", "Ciudad", "Eliminar"};
2
Object[][] data = {
3
{"Juan", 25, "Salta", false},
4
{"María", 30, "Corrientes", false},
5
{"Carlos", 22, "San Juan", false}
6
};
7
8
ModeloTablaPersonalizado model = new ModeloTablaPersonalizado(data, columnNames);
9
JTable table = new JTable(model);
10
11
// Configurar ComboBox para la columna "Ciudad"
12
String[] ciudades = {"Salta", "Corrientes", "San Juan", "Buenos Aires"};
13
JComboBox<String> comboBox = new JComboBox<>(ciudades);
14
table.getColumnModel().getColumn(2).setCellEditor(new DefaultCellEditor(comboBox));
15
16
// La columna "Eliminar" ya será un `CheckBox` debido a nuestro ModeloTablaPersonalizado

Manejar acciones basadas en los cambios en la tabla

Para manejar acciones como eliminar una fila cuando se marca el CheckBox, necesitamos un enfoque que mantenga la consistencia entre nuestros datos originales y los datos mostrados en la tabla. Vamos a implementar esto usando un ArrayList para almacenar nuestros datos y un TableModel personalizado que refleje estos datos.

Primero, definamos una clase para representar nuestros datos:

1
public class Persona {
2
private String nombre;
3
private int edad;
4
private String ciudad;
5
private boolean eliminar;
6
7
public Persona(String nombre, int edad, String ciudad) {
8
this.nombre = nombre;
9
this.edad = edad;
10
this.ciudad = ciudad;
11
this.eliminar = false;
12
}
13
14
// Getters y setters
15
// ...
16
}

Ahora, creamos un TableModel personalizado que use un ArrayList de Persona:

1
import javax.swing.table.AbstractTableModel;
2
import java.util.ArrayList;
3
import java.util.List;
4
5
public class PersonaTableModel extends AbstractTableModel {
6
private List<Persona> personas;
7
private String[] columnNames = {"Nombre", "Edad", "Ciudad", "Eliminar"};
8
9
public PersonaTableModel() {
10
personas = new ArrayList<>();
11
}
12
13
public void addPersona(Persona persona) {
14
personas.add(persona);
15
fireTableRowsInserted(personas.size() - 1, personas.size() - 1); // Notificar a la tabla que se ha añadido una nueva fila
16
}
17
18
public void removePersona(int index) {
19
if (index >= 0 && index < personas.size()) {
20
personas.remove(index);
21
fireTableRowsDeleted(index, index); // Notificar a la tabla que se ha eliminado una fila
22
}
23
}
24
25
@Override
26
public int getRowCount() {
27
return personas.size();
28
}
29
30
@Override
31
public int getColumnCount() {
32
return columnNames.length;
33
}
34
35
@Override
36
public String getColumnName(int column) {
37
return columnNames[column];
38
}
39
40
@Override
41
public Class<?> getColumnClass(int columnIndex) {
42
if (columnIndex == 3) {
43
return Boolean.class; // Columna "Eliminar"
44
}
45
if (columnIndex == 1) {
46
return Integer.class; // Columna "Edad"
47
}
48
// Las demás columnas son de tipo String
49
return super.getColumnClass(columnIndex);
50
}
51
52
@Override
53
public boolean isCellEditable(int rowIndex, int columnIndex) {
54
return true;
55
}
56
57
@Override
58
public Object getValueAt(int rowIndex, int columnIndex) {
59
Persona persona = personas.get(rowIndex);
60
switch (columnIndex) {
61
case 0: return persona.getNombre();
62
case 1: return persona.getEdad();
63
case 2: return persona.getCiudad();
64
case 3: return persona.isEliminar();
65
default: return null;
66
}
67
}
68
69
@Override
70
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
71
Persona persona = personas.get(rowIndex);
72
switch (columnIndex) {
73
case 0:
74
persona.setNombre((String) aValue);
75
break;
76
case 1:
77
persona.setEdad((Integer) aValue);
78
break;
79
case 2:
80
persona.setCiudad((String) aValue);
81
break;
82
case 3:
83
persona.setEliminar((Boolean) aValue);
84
break;
85
}
86
fireTableCellUpdated(rowIndex, columnIndex); // Notificar a la tabla que se ha actualizado una celda
87
}
88
}

Ahora, podemos usar este modelo en nuestra JTable:

1
PersonaTableModel model = new PersonaTableModel();
2
model.addPersona(new Persona("Juan", 25, "Salta"));
3
model.addPersona(new Persona("María", 30, "Corrientes"));
4
model.addPersona(new Persona("Carlos", 22, "San Juan"));
5
6
JTable table = new JTable(model);
7
8
// Configurar ComboBox para la columna "Ciudad"
9
String[] ciudades = {"Salta", "Corrientes", "San Juan", "Buenos Aires"};
10
JComboBox<String> comboBox = new JComboBox<>(ciudades);
11
table.getColumnModel().getColumn(2).setCellEditor(new DefaultCellEditor(comboBox));
12
13
// Manejar la eliminación de filas
14
table.getModel().addTableModelListener(e -> {
15
if (e.getColumn() == 3) { // Columna "Eliminar"
16
int row = e.getFirstRow();
17
Boolean checked = (Boolean) table.getModel().getValueAt(row, 3);
18
if (checked) {
19
model.removePersona(row);
20
}
21
}
22
});

Este enfoque ofrece varias ventajas:

  1. Consistencia de datos: Los datos en la tabla están directamente vinculados a nuestros objetos Persona, asegurando que cualquier cambio en la tabla se refleje en nuestros datos subyacentes.

  2. Tipo de seguridad: Al usar una clase Persona, tenemos un mejor control sobre los tipos de datos en cada columna.

  3. Flexibilidad: Podemos fácilmente añadir o modificar propiedades de Persona y reflejar estos cambios en la tabla.

  4. Separación de preocupaciones: La lógica de los datos está encapsulada en el modelo de la tabla, separándola de la lógica de presentación.