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 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:
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
Persona
y 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.