A veces cuando programamos en varios lenguajes, no podemos impedir comparar el modo de hacer entre unos y otros y uno de ellos es el uso de un control JTable y su relleno con DefaultTableModel
. Comparado con un DataGrid
en WPF al que le pasamos la colección de objetos y si así lo deseamos, las columnas pueden generarse solas, nuestro JTable
está un poco lejos ya que hay que crear las columnas y pasarle un array de objetos por fila, pero por ejemplo al seleccionar una fila, como el objeto no está incluido en ella,sino un array, hay que recomponer de nuevo el objeto, en fin, demasiados inconvenientes.
Para solucionar esto, vamos a crear un objeto DefaultTableModel
al que le pasaremos como argumentos la lista de objetos, los nombres de las columnas, el valor de la key para localizar objetos y un texto para filtros y con todo esto añadiremos algunas funciones adicionales como filtrar u obtener un objeto.
Para generalizar, he creado una interface ObjectTableable
para el objeto en sí, que implementa tres métodos:
Object[] toArray()
. Obtiene un array del objeto con el fin de añadirlo a cada filaint GetIndex()
. Nos informa de la columna que contiene el key del objeto y con el cual podemos luego capturar.Boolean Contains(String text)
. Comprueba en sus propiedades si contiene un texto
public interface ObjectTableable{ public Object[] toArray(); public Object getIndex(); public Boolean contains(String text); }
La siguiente interface la implementará nuestra clase extendida DefaultTableModel
a la cual le pasamos un objeto genérico que implementa la interface ObjectTableable, con esto nos aseguramos que todos los objetos de nuestra clase extendida funciona por igual. Esta interface se llama Tableable y contiene los siguientes métodos:
int getKeyIndex()
. Devuelve el número de columna que contiene el key del objeto en la listavoid setKeyIndex(int index)
. Asigna el valor de la columna keyT getRowObject()
. Devuelve un objeto que implementa la interfazObjectTableable
ArrayList getColumns()
. Obtiene una lista de las columnasvoid setColumns(String [] columns)
. Asigna el valor de las columnasvoid clearColumns()
. Borra las columnas. Esto solo se puede hacer desde el objetojTable.ColumnTableModel
.void filter(String text)
. Filtra el contenido de la colección mediante un texto.
public interface Tableable{ int getKeyIndex(); void setKeyIndex(int index); ArrayList getColumns(); void setColumns(String [] columns); void clearColumns(); T getRowObject(int _row); void filter(String text); }
Un ejemplo de un un objeto que implementa la interfaz ObjectTableable
sería este:
public class Cliente implements ObjectTableable { // campos y métodos //... @Override public Object[] toArray() { return new Object[]{ getCustomerNumber(), getCustomerName(), getContactFirstName(), getContactLastName(), getPhone(), getAddressLine1(), getAddressLine2(), getCity(), getState(), getPostalCode(), getCountry(), getSalesRepEmployeeNumber(), getCreditLimit() }; } @Override public Object getIndex() { return 0; } @Override public Boolean contains(String text) { String lowerText=text.toLowerCase(); return this.getCustomerName().toLowerCase().contains(lowerText) || String.valueOf(getCustomerNumber()).contains(lowerText) || this.getContactFirstName().toLowerCase().contains(lowerText) || this.getContactLastName().toLowerCase().contains(lowerText); } }
El método toArray()
, devuelve un array de los objetos que pretendemos visualizar y contains le indicamos si en alguno de sus campos contiene el elemento que buscamos.
Y ahora la extensión de TableDefaultModel
, la cual debe tratar objetos genéricos para que nos sirva para cualquier objeto que implemente nuestra initerfaz.
public class ExtendedTableModel<T extends ObjectTableable> extends DefaultTableModel implements Tableable{ ArrayList<T> objectList; ArrayList<String> columns; int keyIndex=0; public ExtendedTableModel(ArrayList<T> _list, String[] _columns){ this(_list,_columns,0, ""); } public ExtendedTableModel(ArrayList<T> _list, String[] _columns, int _key){ this(_list,_columns,_key,""); } public ExtendedTableModel(ArrayList<T> _list, String[] _columns, int _key, String text){ objectList=new ArrayList<>(); columns=new ArrayList<>(); keyIndex=_key; objectList.addAll(_list); setColumns(_columns); if (!text.isEmpty()) { filter(text); } setModel(); } void setModel(){ setColumns(); objectList.stream().forEach((object) -> { this.addRow(object.toArray()); }); } void setColumns(){ getColumns().stream().forEach((column) -> { this.addColumn(column); }); } @Override public T getRowObject(int row){ Object keyRow=getValueAt(row, keyIndex); for (T object : objectList) { Object a= object.getIndex(); if (object.getIndex().equals(keyRow)) { return object; } } return null; } @Override public int getKeyIndex() { return keyIndex; } @Override public void setKeyIndex(int index) { keyIndex=index; } @Override public ArrayList<String> getColumns() { return columns; } @Override public void setColumns(String[] _columns) { columns.addAll(Arrays.asList(_columns)); } @Override public void clearColumns() { setColumns(new String[0]); } @Override public void filter(String text){ Object[] temp=objectList.stream().filter(k->k.contains(text)).toArray(); objectList.clear(); for (Object object : temp) { objectList.add((T)object); } }
al instanciar el objeto, ya se crea el modelo con las columnas y filas idenpendientemente del tipo de objeto siempre implemente nuestra interfaz, pero si hacemos dobleclick sobre el jTable
, podemos llamar al método getRowObject
que devolverá el objeto completo o si le passamos como parámetro un texto, iniciará un filtrado sobre los objetos mostrando solo los que cumplan el criterio.Con esto, simulamos un itemsource a un control jTable de Java, primero obtenemos los datos, segundo las columnas, el texto de filtrado y la columna del indice.
ArrayList empleados=negocio.getEmpleados(); String [] _columnsE=new String[] { "Cargo", "Código empleado", "Nombre", "Apellidos","Extensión", "Email", "Oficina"}; etm=new ExtendedTableModel(empleados,_columnsE,1, jTextField1.getText());
Y el resultado.