Valores duplicados de JTable en la fila
Tengo un JTable
formulario personalizado DataModel
(pegado a continuación) y cuando llamo al populate()
método, parece llenar la tabla con datos duplicados: cada fila se completa con el mismo valor una y otra vez. Sin embargo, tras una inspección más cercana (simplemente imprimiendo() el campo 'datos'), el modelo de datos no tiene la culpa: contiene datos correctos, en el formato que espero. ¿Lo que da?
import java.util.ArrayList;
import javax.swing.table.AbstractTableModel;
@SuppressWarnings("serial") // we don't expect this app to ever use serialized classes. EVER.
public class CollectionDataModel extends AbstractTableModel {
private ArrayList<ArrayList<String>> data;
public CollectionDataModel() {
data = new ArrayList<ArrayList<String>>();
}
@Override
public int getColumnCount() {
if(data.isEmpty()) return 0;
return data.get(0).size();
}
@Override
public int getRowCount() {
return data.size();
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
if(rowIndex > getRowCount()) return null;
if(columnIndex > getColumnCount()) return null;
return data.get(rowIndex).get(columnIndex);
}
public void populate(Collection c) {
data.clear();
for(Item i : c.getItems()) {
ArrayList<String> row = new ArrayList<String>();
for(Property p : i.getProperties().values()) {
row.add(p.toString());
}
data.add(row);
}
fireTableDataChanged();
}
}
A continuación se muestra un ejemplo completo que puede resultar útil. Como el ejemplo no se puede modificar, le remito al ejemploMap
de @mKorbel sobre cómo anular y .isCellEditable()
setValueAt()
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.util.Map;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
/** @see https://stackoverflow.com/questions/9132987 */
public class EnvTableTest extends JPanel {
public EnvTableTest() {
this.setLayout(new GridLayout());
this.add(new JScrollPane(new JTable(new EnvDataModel())));
}
private static class EnvDataModel extends AbstractTableModel {
private Map<String, String> data = System.getenv();
private String[] keys;
public EnvDataModel() {
keys = data.keySet().toArray(new String[data.size()]);
}
@Override
public String getColumnName(int col) {
if (col == 0) {
return "Key";
} else {
return "Value";
}
}
@Override
public int getColumnCount() {
return 2;
}
@Override
public int getRowCount() {
return data.size();
}
@Override
public Object getValueAt(int row, int col) {
if (col == 0) {
return keys[row];
} else {
return data.get(keys[row]);
}
}
}
private void display() {
JFrame f = new JFrame("EnvTableTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new EnvTableTest().display();
}
});
}
}
Se podría intentar hacer que los cambios de población sean más atómicos.
public void populate(Collection c) {
ArrayList<ArrayList<String>> data2 = new ArrayList<ArrayList<String>>();
for(Item i : c.getItems()) {
ArrayList<String> row = new ArrayList<String>();
for(Property p : i.getProperties().values()) {
row.add(p.toString());
}
data2.add(row);
}
data = data2;
fireTableDataChanged();
}
Supongo que populate
se vuelve a llamar antes de que populate
finalice una llamada anterior. Y probablemente c
cambie durante su iteración.
1) TableModel
no está completo, extraño muchos métodos requeridos para JTable's
el ciclo de vida, comenzando con TableHeader
etc.
2) dado que hay muchos AbstactTableModels basados en HashMap, sugeriría devolver el tipo de matrices implementado en API directamente
Vector<Vector<Object or String>> data;
String[][] or Object[][]
en lugar de
ArrayList<ArrayList<String>> data;
Una explicación simple es que XxxList devuelve la columna y Vector o String[] devuelve la fila.
3) Sugeriría usarlo DefaultTableModel
directamente, así nunca necesitará resolver duplicados o columnas/filas perdidas