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:
1import javax.swing.*;2import javax.swing.table.DefaultTableModel;3
4public 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:
1import java.awt.Color;2import java.awt.Component;3import javax.swing.table.DefaultTableCellRenderer;4
5public class ResaltadoRenderer extends DefaultTableCellRenderer {6 private int filaSeleccionada = -1;7 private int columnaSeleccionada = -1;8
9 @Override10 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 fondo15 } else {16 c.setBackground(table.getBackground()); // restauramos el color de fondo predeterminado17 }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:
1ResaltadoRenderer renderer = new ResaltadoRenderer();2table.setDefaultRenderer(Object.class, renderer);3
4table.getSelectionModel().addListSelectionListener(e -> {5 if (!e.getValueIsAdjusting()) { // Si la selección ha terminado6 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:
1import javax.swing.table.DefaultTableModel;2
3public class ModeloTablaPersonalizado extends DefaultTableModel {4 public ModeloTablaPersonalizado(Object[][] data, String[] columnNames) {5 super(data, columnNames);6 }7
8 @Override9 public Class<?> getColumnClass(int columnIndex) {10 if (columnIndex == 3) { // Columna de CheckBox11 return Boolean.class;12 }13 return super.getColumnClass(columnIndex);14 }15
16 @Override17 public boolean isCellEditable(int row, int column) {18 return true; // Todas las celdas son editables19 }20}Ahora, configuramos la tabla con este modelo y añadimos un ComboBox editor para una columna específica:
1String[] columnNames = {"Nombre", "Edad", "Ciudad", "Eliminar"};2Object[][] data = {3 {"Juan", 25, "Salta", false},4 {"María", 30, "Corrientes", false},5 {"Carlos", 22, "San Juan", false}6};7
8ModeloTablaPersonalizado model = new ModeloTablaPersonalizado(data, columnNames);9JTable table = new JTable(model);10
11// Configurar ComboBox para la columna "Ciudad"12String[] ciudades = {"Salta", "Corrientes", "San Juan", "Buenos Aires"};13JComboBox<String> comboBox = new JComboBox<>(ciudades);14table.getColumnModel().getColumn(2).setCellEditor(new DefaultCellEditor(comboBox));15
16// La columna "Eliminar" ya será un `CheckBox` debido a nuestro ModeloTablaPersonalizadoManejar 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:
1public 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 setters15 // ...16}Ahora, creamos un TableModel personalizado que use un ArrayList de Persona:
1import javax.swing.table.AbstractTableModel;2import java.util.ArrayList;3import java.util.List;4
5public 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 fila16 }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 fila22 }23 }24
25 @Override26 public int getRowCount() {27 return personas.size();28 }29
30 @Override31 public int getColumnCount() {32 return columnNames.length;33 }34
35 @Override36 public String getColumnName(int column) {37 return columnNames[column];38 }39
40 @Override41 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 String49 return super.getColumnClass(columnIndex);50 }51
52 @Override53 public boolean isCellEditable(int rowIndex, int columnIndex) {54 return true;55 }56
57 @Override58 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 @Override70 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 celda87 }88}Ahora, podemos usar este modelo en nuestra JTable:
1PersonaTableModel model = new PersonaTableModel();2model.addPersona(new Persona("Juan", 25, "Salta"));3model.addPersona(new Persona("María", 30, "Corrientes"));4model.addPersona(new Persona("Carlos", 22, "San Juan"));5
6JTable table = new JTable(model);7
8// Configurar ComboBox para la columna "Ciudad"9String[] ciudades = {"Salta", "Corrientes", "San Juan", "Buenos Aires"};10JComboBox<String> comboBox = new JComboBox<>(ciudades);11table.getColumnModel().getColumn(2).setCellEditor(new DefaultCellEditor(comboBox));12
13// Manejar la eliminación de filas14table.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:
-
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. -
Tipo de seguridad: Al usar una clase
Persona, tenemos un mejor control sobre los tipos de datos en cada columna. -
Flexibilidad: Podemos fácilmente añadir o modificar propiedades de
Personay reflejar estos cambios en la tabla. -
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.