domingo, 10 de marzo de 2013

Implementación y sobrecarga de un TableModel personalizado en JAVA

Cuando vamos a presentar datos que son muy parecidos a EXCEL usamos un tabla y en JAVA usamos el jXTable, pero por lo general usamos un TablaModel básico, es decir, no se ajusta a las caracteristicas que deseamos y en vez de facilitarnos la vida, nos la complica.

Aquí un ejemplo de un TableModel:

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

public class MiTableModel extends AbstractTableModel {
  
    public boolean cellEditable = false;
    public List dataArray;
    public int[] columneditable=null;
    public ArrayList columnIdentifiers;

    @Override
    public int getRowCount() {
        return dataArray.size();
    }
    @Override
    public int getColumnCount() {
        return columnIdentifiers.size();
    }
    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
         return dataArray.get(rowIndex);
    }
    @Override
    public String getColumnName(int column) {
        return columnIdentifiers.get(column);
    }
    public ArrayList getColumnIdentifiers() {
        return columnIdentifiers;
    }
    public void removeRow(int row) {
        dataArray.remove(row);
        fireTableRowsDeleted(row, row);
    }
    public void addRow(Object data){
        this.dataArray.add(data);
        fireTableDataChanged();
    }
    public Object getRow(int index){
        return dataArray.get(index);
    }

    public List getData() {
        return dataArray;
    }

    public void setData(List dataArray) {
        this.dataArray = dataArray;
    }
}


Ahora ya tenemos la clase base de nuestro TableModel, pero quiero un TableModel más personalizado y que se ajuste a mi necesidad, pues lo que haremos es lo siguiente:

Crearemos una clase especial que utilizará mi clase anterior como base.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class TMPersonalizado extends MiTableModel {
    
    private String[] columnasNombres;
    
    public TMPersonalizado() {
        headerDefault();
        initTable();
    }
    
    public TMPersonalizado(String[] cn) {
        columnasNombres = cn;
        initTable();
    }
    
    private void headerDefault() {
        columnasNombres = new String[2];
        columnasNombres[0] = "Una columna. 1";
        columnasNombres[1] = "Una columna. 2";
    }
    
    public void updateTable(){
        //dataArray = ManejoBanco.getInstancia().getLista();
        fireTableDataChanged();
    }
    
    private void initTable(){
        ArrayList header = new ArrayList();
        header.addAll(Arrays.asList(columnasNombres));
        
        columnIdentifiers = header;
        dataArray = new ArrayList();
        fireTableDataChanged();
    }
    
    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        MisDatos misDatos = (MisDatos) dataArray.get(rowIndex);
        switch(columnIndex){
            case 0:
                return misDatos.getMiDato1();
            case 1:
                return misDatos.getMiDato2();
            default:
                return null;
        }
    }
}

Como verán creo un objeto con mis propios constructores y asigno los datos que deseo, pero si se fijan hago uso de un modelo de datos de representación de filas de una base de datos y lo uso en mi TableModel, para facilitarme la vida a la hora de manipular los datos basados en un objeto POJO de JAVA. Si no saben lo que es un objeto POJO de JAVA, pues en un intento de definirlo vulgarmente es una clase que sigue ciertas reglas establecidas y  no extiende de nada. Aqui un ejemplo del modelo que use:

public class MisDatos {
    private int miDato1;
    private String miDato2;

    public int getMiDato1() {
        return miDato1;
    }
    public void setMiDato1(int miDato1) {
        this.miDato1 = miDato1;
    }
    public int getMiDato2() {
        return miDato2;
    }

    public void setMiDato2(int miDato2) {
        this.miDato2 = miDato2;
    }
}

y por ultimo un ejemplo de como instanciar y asignar el TableModel a una tabla:

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.jdesktop.swingx.JXTable;

public class Prueba {
    private List dats = new ArrayList();
    private TMPersonalizado tm = new TMPersonalizado();
    private JXTable mitabla;

    public Prueba() {
        mitabla = new JXTable();
        tm.setData(dats);
        mitabla.setModel(tm);
    }
}

Como verán es muy sencillo, pero y como consigo los datos de los cuales fueron seleccionados?, pues conseguir los datos seria algo así

Esto supone ser un método dentro de la clase anterior.

    public MisDatos captura() {
        MisDatos fila = (MisDatos) tm.getRow(mitabla.getSelectedRow());
        return fila;
    }

Excelente, pero y como obtengo la fila cuando se ordenan los datos?

    public MisDatos capturaOrdenada() {
        MisDatos fila = (MisDatos) tm.getRow(mitabla.convertRowIndexToModel(mitabla.getSelectedRow()));
        return fila;
    }

Ya con esto tenemos un modelo de un TableModel funcional.

Que haríamos sin @Override?

Cuando programamos tendemos a escribir código con funcionalidades específicas y en combinación con piezas, libs o apis aveces estas no traen lo que necesitamos y por ignorancia o otra razón tendemos a reinventar la rueda.

Es por eso que se creo @Override en los lenguajes de programación POO, para convertir algo que no hace lo que queremos en lo que buscamos.

Un ejemplo claro es cuando tenemos un carro, si el carro no tiene el motor que queremos solo reemplazamos el motor y no el carro completo(reinventar la rueda) y esto es más o menos un ejemplo de @Override en la vida real.

Muchas veces es mejor sobrecargar un método que escribir la clase completa, ya que podríamos añadir métodos específicos de nuestra necesidad si es que no contempla esas caracteristicas el objeto que estamos usando.