Por favor, activa JavaScript y desactiva tu adblock para este sitio

El Javatar

Blog dedicado a la Programación en Java, C, PHP, Phyton, HTML, SQL y Mucho Más

lunes, 21 de marzo de 2016

Cómo Hacer un Modelo de Datos Personalizado para un JTable en Java

Una de las mejores formas de organizar y mostrar la información que obtenemos ya sea de una Base de Datos o de alguna otra fuente de datos, o incluso información estática en nuestros programas es a través de tablas, y en este artículo veremos como crear modelos de datos personalizados para no sólo mostrar información en un JTable en Java, sino también para acceder a la información que se encuentra en ellos, e incluso para modificarla de forma sencilla en caso de que así lo necesitemos.

Cómo Hacer un Modelo de Datos Personalizado para un JTable en Java

Cuando queremos mostrar información en un JTable, normalmente lo hacemos a través de una clase del API de Java: DefaultTableModel, que sería la forma más sencilla de hacerlo, sin embargo, desde que aprendí esta forma que les enseñaré a continuación, es decir, usando un Modelo de Datos personalizado, procuro tratar de usarlo, ya que como su nombre lo indica, lo que hacemos es crear un Modelo de Datos, que separa el Modelo de la Vista, de esta forma, lo único que haremos es manipular siempre nuestro modelo, y la vista se actualizará de acuerdo a este.

Uso de Modelos de Datos para JTable en Java


Bien, primero que nada, veamos un poco de teoría... sí, lo se, a veces no nos gusta mucho la teoría, pero muchas veces es necesaria para entender lo que estamos haciendo, sin embargo, vamos a explicar la teoría de una forma sencilla y enfocándonos en lo que nos interesa para este tutorial :-)

Cuando vamos a gestionar los datos dentro de un JTable, siempre debemos hacerlo a través de un modelo de datos, y cómo había mencionado anteriormente, lo más sencillo es hacerlo a través de un DefaultTableModel, ya que lo único que haríamos es lo siguiente:

DefaultTableModel modelo = new DefaultTableModel();
JTable tabla = new JTable(modelo);

O en caso de que ya tuviésemos nuestra instancia de JTable creada como sucede cuando trabajamos con el editor gráfico de NetBeans, asignaríamos nuestro modelo de la siguiente forma:

DefaultTableModel modelo = new DefaultTableModel();
tabla.setModel(modelo);

Y esta es la forma más sencilla, puesto que la clase DefaultTableModel implementa todos los métodos de la interface TableModel, y de la clase abstracta AbstractTableModel, brindándonos un comportamiento por defecto para todos nuestros JTables e incluso permitiéndonos sobre-escribir sus métodos para casos en los que necesitemos comportamientos más personalizados.

Esto nos puede ser práctico en caso de que sólo deseáramos mostrar datos que no van a cambiar, por ejemplo, una tabla con valores estáticos, a la cual no nos interesa acceder a sus datos y en la que por ejemplo sólo nos interesara que no se permitiera editar el valor de sus celdas. Esto lo haríamos más o menos de la siguiente forma:

DefaultTableModel modelo = new DefaultTableModel() {
    @Override
    public boolean isCellEditable (int row, int column){
        return false;
    }
};
tabla.setModel(modelo);
// Podemos añadir los datos antes o después de asignar el modelo
// Supongamos que añadimos 10 filas de datos
for (int i = 0; i < 10; i++) {
    modelo.addRow(new Object[]{"Columna 1", "Columna 2", "Columna 3", 1000, 50.8});
}

Esto es totalmente válido, y como vemos, muy rápido de hacer, y hasta podríamos decir que un poco práctico, aunque la verdad no tanto, y ya veremos el por qué, a menos claro, que como lo había mencionado, sólo nos interese mostrar los datos y nada más dejarlos ahí quieticos, incluso sin querer acceder a ellos, lo cual es algo que normalmente no suele suceder.

Empecemos a plantear algunos casos. Primero, supongamos que deseamos obtener los datos de una fila para una operación, o para pasarlo a algún método, o alguna otra tarea que necesitemos; en este caso supongamos que es para mostrarlo en un String; para esto haríamos lo siguiente:

int fila = tabla.getSelectedRow();
String valores = "";
for (int i = 0; i < tabla.getColumnCount(); i++) {
    valores += tabla.getValueAt(fila, i).toString() + ", ";
}

El método getValueAt(row, column) devuelve un dato de tipo Object, por eso usamos su método toString() para pasarlo a String. Hasta aquí, todo está normal y se puede hacer sin ningún problema, aunque desde ya podemos visualizar que acceder a los datos de un JTable a través de este método implica perder comunicación con nuestras clases Bean, en caso de que las usemos para crear las instancias de los datos, ya veremos más adelante a lo que me refiero.

Bueno, de igual modo, si deseamos insertar algún valor, o más bien, modificar algún valor que ya tenemos dentro del JTable lo haríamos con el método:

...
tabla.setValueAt(value, row, column);
...

Bueno, pero vayamos un poco más allá. Supongamos que por ejemplo en las filas en las que agregamos valores numéricos, nosotros deseamos que éstos se vean con un formato específico, por ejemplo con el formato #.##0,## Esto lo haríamos así:

DecimalFormatSymbols simbolos = new DecimalFormatSymbols();
simbolos.setDecimalSeparator(',');
simbolos.setGroupingSeparator('.');
DecimalFormat formato = new DecimalFormat("#,##0.##",simbolos);

for (int i = 0; i < 10; i++) {
    modelo.addRow(new Object[]{"Columna 1", "Columna 2", "Columna 3", formato.format(1000), formato.format(50.8)});
}

Bien, parece que ahora nuestros valores numéricos se muestran un poco más bonitos al tener un formato específico. ¿Pero qué pasaría si nosotros deseamos obtener todos los valores de una columna numérica para por ejemplo hacer una suma con ellos?

El método getValueAt(row, column) nos devuelve un objeto el cual podemos castear al tipo de dato que el compilador reconoce para los datos que el JTable encuentra en esa columna. Así que si deseamos obtener por ejemplo un double, lo haríamos así:

double valor = (double) tabla.getValueAt(row, column);
// También podemos hacerlo a través de esta otra forma:
double valor2 = Double.parseDouble(tabla.getValueAt(row, column).toString());

Si el valor que se obtiene es en efecto un double, no habrá ningún problema, pero en caso de no serlo, se nos arrojará una Excepción en tiempo de ejecución de tipo ClassCastException para el primer caso, o de NumberFormatException para el segundo, lo cual es lo que muy seguro nos pasará ahora.

¿Por qué estoy seguro que tendremos una excepción? Si no me crees, haz una prueba como te he dicho y aplicando los formatos que indico anteriormente y me cuentas jejeje ;-)

Pues bien, recuerda que hemos añadido un formato, y al ingresar un número formateado, no estamos ingresando en el JTable un número, sino un String, así que si intentamos hacer un casting de String a double, obtendremos una Excepción.

Bien, ahora me dirás, ¿y si uso la segundo forma, es decir, haciendo un parsing usando los métodos respectivos de las clases de tipo numérico? Esto puede funcionar, claro, si la cadena obtenida tiene el formato adecuado.

Sin embargo, éste no es el caso, porque para Java un número almacenado en un String puede ser convertido a Integer si éste no posee ningún separador, y a Double si sólamente tiene el separador de decimales, el cual es por defecto el ".", y recuerda que en los formatos hemos indicado que nuestros valores se mostrarán con separador de miles, y además el separador de decimales que hemos asignado es la ",".

Vaya, menudo problema, y no estoy diciendo que no tenga solución si lo hiciéramos así, pero dicha solución sería muy artificiosa, ya que cada vez que obtengamos un valor, primero tendríamos que obtener el String, eliminarle el separador de miles, y posteriormente cambiarle el símbolo decimal, lo cual es un derroche de recursos que sólo hace que nuestra aplicación vaya más lenta.

Así que por estas, y por muchas otras razones, la mejor solución es usar un Modelo de datos que tenga independencia de nuestra JTable. para lo cual crearemos una clase que extienda de la clase AbstractTableModel. También podríamos hacerlo implementando la clase TableModel, sin embargo, al heredar de la clase AbstractTableModel evitamos tener que sobre-escribir los métodos para notificar a los suscriptores, además de que nos evitamos tener que crear un TableModelEvent, rellenarlo con los datos adecuados y avisar a los suscriptores cuando hacemos alguna modificación en los datos.

Un suscriptor es cualquier clase que quiera enterarse de cambios en los datos del modelo. El JTable es un ejemplo claro. El JTable se suscribe a cambios de datos en el modelo y de esta forma, en cuanto cambiemos datos en el modelo, el JTable se entera y se repinta automáticamente la pantalla.

Bien, yo creo que ya es suficiente de teoría y podemos dejar hasta aquí esta introducción. Entremos ahora sí en materia de este tutorial que es aprender cómo hacer un modelo de datos personalizados para un JTable en Java:

¿Qué Usaremos Para Este Tutorial?
- NetBeans 8.1
- Java 1.8

Nivel: Intermedio

Tiempo: 30 minutos

1. Creamos un proyecto en NetBeans con la siguiente estructura:

Estructura de Proyecto Ejemplo AbstractTableModel para JTable Java

Aquí podemos observar 3 clases. VentanaPrincipal, que es la clase Main y en la que tendremos nuestros componentes visuales como nuestro JTable, la clase ModeloDatosTablaPersonas que será nuestro Modelo de Datos para el JTable, y nuestra clase Persona que será el Bean de nuestro Modelo de Datos.

2. Creamos la interfaz de usuario la cual comprende un JFrame donde hemos agregado un JTable y un JButton que usaremos para obtener los datos de una fila seleccionada.

Interfaz Grafica del Ejemplo AbstractTableModel para JTable en Java

3. Creamos el Bean de nuestro modelo de acuerdo a la estructura de esta JTable. Así que nuestra clase Persona nos quedaría de la siguiente forma:

package com.eljavatar.ejemploabstracttablemodel;

/**
 *
 * @author Andres
 */
public class Persona {
    
    private String codigo;
    private String nombres;
    private int edad;
    private double valorDeuda;

    public Persona(String codigo, String nombres, int edas, double valorDeuda) {
        this.codigo = codigo;
        this.nombres = nombres;
        this.edad = edas;
        this.valorDeuda = valorDeuda;
    }

    public Persona() {
    }

    public String getCodigo() {
        return codigo;
    }

    public void setCodigo(String codigo) {
        this.codigo = codigo;
    }

    public String getNombres() {
        return nombres;
    }

    public void setNombres(String nombres) {
        this.nombres = nombres;
    }

    public int getEdad() {
        return edad;
    }

    public void setEdad(int edad) {
        this.edad = edad;
    }

    public double getValorDeuda() {
        return valorDeuda;
    }

    public void setValorDeuda(double valorDeuda) {
        this.valorDeuda = valorDeuda;
    }
    
}

En ésta clase sólamente hemos creado los parámetros que necesitamos, dos constructores, uno con todos los atributos de la clase y uno por defecto vacío, y los Getters y Setters de cada uno de los atributos, es decir, una clase POJO común y corriente.

4. Ahora vamos a nuestra clase ModeloDatosTablaPersona. lo primero que debemos hacer es heredar de la clase AbstractTableModel, por lo cual debemos importar dicha clase, pero además, ésta posee tres métodos abstractos los cuales debemos implementar; así pues, nuestra clase empezaría teniendo la siguiente forma:

package com.eljavatar.ejemploabstracttablemodel;

import javax.swing.table.AbstractTableModel;

public class ModeloDatosTablaPersonas extends AbstractTableModel {

    @Override
    public int getRowCount() {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public int getColumnCount() {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
    }

}

En un momento iremos editando el código de estos tres métodos, por ahora seguiremos con lo siguiente:

5. Creamos tres atributos. Un array de tipo Class que contendrá el tipo de dato de las columnas, un array de tipo String que contendrá los títulos de las columnas, y una lista de tipo List<Persona> que contendrá los datos de nuestro JTable. Además crearemos el respectivo Get y Set para la lista de datos:

private final Class[] tipoColumnas;
private final String[] titleColumnas;
private List<Persona> personas;

public List<Persona> getPersonas() {
    return personas;
}

public void setPersonas(List<Persona> personas) {
    this.personas = personas;
}

6. Ahora creamos el constructor de la clase en donde inicializaremos la lista y asignaremos los nombres de las columnas y sus correspondientes tipos de datos:

public ModeloDatosTablaPersonas() {
    personas = new ArrayList();
    this.titleColumnas = new String[]{"Código", "Nombres", "Edad", "Valor Deuda"};
    this.tipoColumnas = new Class[]{String.class, String.class, Integer.class, Double.class};
}

7. Ahora sobre-escribiremos tres métodos de la clase AbstractTableModel mediante los cuales retornaremos el título de nuestras columnas, su tipo de dato, y además modificaremos el comportamiento por defecto de la edición de las celdas para que éstas puedan ser editadas:

@Override
public String getColumnName(int column) {
    return titleColumnas[column];
}

@Override
public Class<?> getColumnClass(int columnIndex) {
    return tipoColumnas[columnIndex];
}

@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
     return true;
}

8. Bien, ahora vamos a ver como quedarían los métodos que implementamos inicialmente:

@Override
public int getRowCount() {
    return personas.size();
}

@Override
public int getColumnCount() {
    return titleColumnas.length;
}

Con éstos dos métodos lo que hacemos es obtener la cantidad de filas según la cantidad de elementos que tiene nuestro List, y la cantidad de columnas según la cantidad de elementos que tiene el array que guarda los títulos de las columnas.

Y el método getValueAt(int rowIndex, int columnIndex) nos quedaría de la siguiente forma:

@Override
public Object getValueAt(int rowIndex, int columnIndex) {
    switch (columnIndex) {
        case 0:
            return personas.get(rowIndex).getCodigo();
        case 1:
            return personas.get(rowIndex).getNombres();
        case 2:
            return personas.get(rowIndex).getEdad();
        case 3:
            return personas.get(rowIndex).getValorDeuda();
        default:
            return null;
    }
}

Es con éste método con el que mostramos los datos en el JTable.

9. Para poder cambiar datos en el JTable, debemos sobre-escribir el método setValueAt(Object aValue, int rowIndex, int columnIndex) de la clase AbstractTableModel y hacerlo de forma similar a como lo hicimos con el método getValueAt, pero además, notificamos la modificación en los datos a través de dos métodos de ésta misma clase:

@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
    switch (columnIndex) {
        case 0:
            personas.get(rowIndex).setCodigo(aValue.toString());
            break;
        case 1:
            personas.get(rowIndex).setNombres(aValue.toString());
            break;
        case 2:
            personas.get(rowIndex).setEdad((int) aValue);
            break;
        case 3:
            personas.get(rowIndex).setValorDeuda((double) aValue);
        default: ;
    }
    this.fireTableCellUpdated(rowIndex, columnIndex);
    this.fireTableRowsUpdated(rowIndex, rowIndex);
}

Como podemos apreciar, cuando modificamos entonces ahora un valor en el JTable, este no se afecta directamente en el JTable, sino en la lista de tipo Persona, la cual es la que se encarga de gestionar los datos y no un Object genérico como sucedía cuando usábamos DefaultTableModel.

Bueno, parece que ya todo está tomando forma. Ahora sólo nos falta hacer uso de este modelo, pero antes, veamos cómo quedó completa nuestra clase ModeloDatosTablaPersonas:

package com.eljavatar.ejemploabstracttablemodel;

import java.util.ArrayList;
import java.util.List;
import javax.swing.table.AbstractTableModel;

/**
 *
 * @author Andres
 */
public class ModeloDatosTablaPersonas extends AbstractTableModel {

    private final Class[] tipoColumnas;
    private final String[] titleColumnas;
    private List<Persona> personas;

    public List<Persona> getPersonas() {
        return personas;
    }

    public void setPersonas(List<Persona> personas) {
        this.personas = personas;
    }

    public ModeloDatosTablaPersonas() {
        personas = new ArrayList();
        this.titleColumnas = new String[]{"Código", "Nombres", "Edad", "Valor Deuda"};
        this.tipoColumnas = new Class[]{String.class, String.class, Integer.class, Double.class};
    }

    @Override
    public String getColumnName(int column) {
        return titleColumnas[column];
    }

    @Override
    public Class<?> getColumnClass(int columnIndex) {
        return tipoColumnas[columnIndex];
    }

    @Override
    public boolean isCellEditable(int rowIndex, int columnIndex) {
        return true;
    }

    @Override
    public int getRowCount() {
        return personas.size();
    }

    @Override
    public int getColumnCount() {
        return titleColumnas.length;
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        switch (columnIndex) {
            case 0:
                return personas.get(rowIndex).getCodigo();
            case 1:
                return personas.get(rowIndex).getNombres();
            case 2:
                return personas.get(rowIndex).getEdad();
            case 3: 
                return personas.get(rowIndex).getValorDeuda();
            default:
                return null;
        }
    }

    @Override
    public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
        switch (columnIndex) {
            case 0:
                personas.get(rowIndex).setCodigo(aValue.toString());
                break;
            case 1:
                personas.get(rowIndex).setNombres(aValue.toString());
                break;
            case 2:
                personas.get(rowIndex).setEdad((int) aValue);
                break;
            case 3:
                personas.get(rowIndex).setValorDeuda((double) aValue);
            default: ;
        }
        this.fireTableCellUpdated(rowIndex, columnIndex);
        this.fireTableRowsUpdated(rowIndex, rowIndex);
    }

}

10. En nuestra clase VentanaPrincipal que es donde hemos creado nuestro JTable, creamos un atributo de tipo ModeloDatosTablaPersonas y creamos un método en el que asignaremos nuestro respectivo modelo:

private ModeloDatosTablaPersonas modeloTablaPersonas;

private void setModeloTabla() {
    modeloTablaPersonas = new ModeloDatosTablaPersonas();
    modeloTablaPersonas.setPersonas(obtenerPersonas());
    this.jTpersonas.setModel(modeloTablaPersonas);
    this.jTpersonas.getColumnModel().getColumn(3).setCellRenderer(new DecimalFormatRenderer());
}

11. El método obtenerPersonas() es cualquier método que nos retorne un ArrayList con datos ya sea de una base de datos, o de algún otro lado, pero en este caso será una lista de datos que crearemos a lo más Hard Coding :-P

public ArrayList<Persona> obtenerPersonas() {
    ArrayList<Persona> lista = new ArrayList();
    lista.add(new Persona("Cod001", "Juan", 24, 5000.4));
    lista.add(new Persona("Cod002", "Felipe", 27, 6700.8));
    lista.add(new Persona("Cod003", "Diana", 31, 354.9));
    lista.add(new Persona("Cod004", "María", 46, 5300));
    lista.add(new Persona("Cod005", "Camilo", 28, 2890.6));
    lista.add(new Persona("Cod006", "Pedro", 52, 1600.5));
    lista.add(new Persona("Cod007", "Sandra", 43, 2000));
    lista.add(new Persona("Cod008", "Esteban", 39, 752));
    lista.add(new Persona("Cod009", "Pablo", 26, 2430.1));
    lista.add(new Persona("Cod010", "Ana", 38, 3470.3));
    return lista;
}

12. La clase DecimalFormatRenderer es una clase estática dentro de nuestra clase encargada de darle formato a los valores decimales, en este caso de la columna 3:

static class DecimalFormatRenderer extends DefaultTableCellRenderer {
        
    private static DecimalFormatSymbols simbolos;
    private static DecimalFormat formato;
        
    @Override
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
        simbolos = new DecimalFormatSymbols();
        simbolos.setDecimalSeparator(',');
        simbolos.setGroupingSeparator('.');
        formato = new DecimalFormat("#,##0.##", simbolos);
        value = formato.format((Number) value);
        return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
    }
}

Sé lo que algunos estarán pensando, y si.. es cierto, esto mismo lo hubiéramos podido hacer con el DefaultTableModel, pero a poco no es más bonita esta implementación jejejeje... bueno hablando en serio, la mayor ventaja de hacerlo de este modo es que no perdemos la referencia de los datos que instanciamos a través de nuestra clase Bean, lo cual veremos ahora cuando queramos obtener los datos de alguna fila, pero antes, añadamos el método setModeloTabla() a nuestro constructor:

public VentanaPrincipal() {
    initComponents();
    setModeloTabla();
}

13. Vamos a añadir un evento de tipo ActionListener a nuestro botón, para ello, basta con hacer doble clic sobre el botón, y NetBeans automáticamente nos añadirá el evento y nos ubicará dentro del método en el cual escribiremos las acciones que se realizarán cuando se haga clic sobre el botón. Así pues, el código de nuestro botón sería el siguiente:

private void jBobtenerDatosActionPerformed(java.awt.event.ActionEvent evt) {
    int fila = this.jTpersonas.getSelectedRow();
    if (fila < 0) {
        JOptionPane.showMessageDialog(this, "Debe Seleccionar una Fila", "Error", JOptionPane.ERROR_MESSAGE);
        return;
    }
    Persona persona = modeloTablaPersonas.getPersonas().get(fila);
    String mensaje = "Datos de la Persona Seleccionada\n"
            + "Código: " + persona.getCodigo() + "\n"
            + "Nombre: " + persona.getNombres() + "\n"
            + "Edad: " + persona.getEdad() + "\n"
            + "Valor Deuda: " + persona.getValorDeuda();
    JOptionPane.showMessageDialog(this, mensaje, "Datos", JOptionPane.INFORMATION_MESSAGE);
}

Como podemos apreciar, los datos no los estamos recuperando del JTable, sino del List<Persona>, lo cual es por un lado mucho más práctico ya que evitamos tener que castear los datos cuando los recuperamos ahorrando así recursos y agilizando el rendimiento de nuestras aplicaciones, y por otro lado seguimos buenas prácticas de Programación en las que separamos las vistas de los modelos de datos.

Además como lo he venido mencionando a lo largo de éste tutorial, no perdemos la referencia que tenemos con nuestra clase Bean, que en este caso es Persona, ya que cuando lo hacemos a través de un DefaultTableModel, la perdemos desde que ingresamos los datos en el JTable ya que debemos meter nuestros datos en un array de tipo Object para poder hacerlo, y para recuperarlos en un Bean si así lo quisiéramos, tendríamos que recuperar los datos como Objects y castearlos; mientras que con esta forma, ingresamos un objeto de tipo Persona, y de igual forma recuperamos un objeto de tipo Persona.

Así pues, el código completo de nuestra clase VentanaPrincipal, y omitiendo el código auto-generado por NetBeans, nos quedaría así:

package com.eljavatar.ejemploabstracttablemodel;

import java.awt.Component;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import javax.swing.JOptionPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;

/**
 *
 * @author Andres
 */
public class VentanaPrincipal extends javax.swing.JFrame {

    private ModeloDatosTablaPersonas modeloTablaPersonas;

    /**
     * Creates new form VentanaPrincipal
     */
    public VentanaPrincipal() {
        initComponents();
        setModeloTabla();
    }

    public ArrayList<Persona> obtenerPersonas() {
        ArrayList<Persona> lista = new ArrayList();
        lista.add(new Persona("Cod001", "Juan", 24, 5000.4));
        lista.add(new Persona("Cod002", "Felipe", 27, 6700.8));
        lista.add(new Persona("Cod003", "Diana", 31, 354.9));
        lista.add(new Persona("Cod004", "María", 46, 5300));
        lista.add(new Persona("Cod005", "Camilo", 28, 2890.6));
        lista.add(new Persona("Cod006", "Pedro", 52, 1600.5));
        lista.add(new Persona("Cod007", "Sandra", 43, 2000));
        lista.add(new Persona("Cod008", "Esteban", 39, 752));
        lista.add(new Persona("Cod009", "Pablo", 26, 2430.1));
        lista.add(new Persona("Cod010", "Ana", 38, 3470.3));
        return lista;
    }

    private void setModeloTabla() {
        modeloTablaPersonas = new ModeloDatosTablaPersonas();
        modeloTablaPersonas.setPersonas(obtenerPersonas());
        this.jTpersonas.setModel(modeloTablaPersonas);
        this.jTpersonas.getColumnModel().getColumn(3).setCellRenderer(new DecimalFormatRenderer());
    }

    static class DecimalFormatRenderer extends DefaultTableCellRenderer {
        
        private static DecimalFormatSymbols simbolos;
        private static DecimalFormat formato;
        
        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
            simbolos = new DecimalFormatSymbols();
            simbolos.setDecimalSeparator(',');
            simbolos.setGroupingSeparator('.');
            formato = new DecimalFormat("#,##0.##", simbolos);
            value = formato.format((Number) value);
            return super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
        }
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")                       
    private void initComponents() {
     // Código Generado por NetBeans
    }

    private void jBobtenerDatosActionPerformed(java.awt.event.ActionEvent evt) {
        int fila = this.jTpersonas.getSelectedRow();
        if (fila < 0) {
            JOptionPane.showMessageDialog(this, "Debe Seleccionar una Fila", "Error", JOptionPane.ERROR_MESSAGE);
            return;
        }
        Persona persona = modeloTablaPersonas.getPersonas().get(fila);
        String mensaje = "Datos de la Persona Seleccionada\n"
                + "Código: " + persona.getCodigo() + "\n"
                + "Nombre: " + persona.getNombres() + "\n"
                + "Edad: " + persona.getEdad() + "\n"
                + "Valor Deuda: " + persona.getValorDeuda();
        JOptionPane.showMessageDialog(this, mensaje, "Datos", JOptionPane.INFORMATION_MESSAGE);
    }

    /**
     * @param args the command line arguments
     */
    public static void main(String args[]) {
        // Código Generado por NetBeans
    }

    // Variables declaration - do not modify                     
    private javax.swing.JButton jBobtenerDatos;
    private javax.swing.JScrollPane jScrollPane1;
    private javax.swing.JTable jTpersonas;
    // End of variables declaration                   
}

Con esto ya tendríamos todo listo, y si ejecutamos nuestro proyecto obtendremos el siguiente resultado:

Ejecución del Proyecto Ejemplo AbstractTableModel Java

Y si seleccionamos una fila, y damos clic sobre el botón "Obtener Datos de Fila Seleccionada" lo que obtenemos seríamos un JOptionPane que muestra la siguiente información:

Datos de Persona Seleccionada en Ejemplo AbstractTableModel en Java

Bueno, eso es todo por este artículo... creo que me salió un poco largo ;-) jejeje pero bueno, quería que todo quedara lo más claro posible.

Sin embargo, si tienes alguna duda, puedes hacérmela sabre a través de los comentarios y yo trataré de responderla a la mayor brevedad posible.

Si lo deseas, puedes descargar el código fuente de este proyecto en GitHub:

==> Ejemplo AbstractTableModel en GitHub

2 comentarios: