JTable: Eingabe in Tabellenzelle korrigieren

javango

Mitglied
Hallo,
ich hab eine Tabelle erstellt, in die der User Hex-Strings eingeben kann. Diese möchte ich nach dem Editieren korrigieren, bevor sie übernommen wird. Ich habe mir den folgenden Editor geschrieben:
Java:
  class FontCellEditor extends DefaultCellEditor {

    public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) {
      JTextField editor = (JTextField) super.getTableCellEditorComponent(table, value, isSelected, row, column);
      String s = "";  
      if (value != null) {
        s = value.toString();       
        s = new String(Convert.simpleHexStringCorrection(s)); // meine Korrektur-Funktion
        editor.setText(s);
      }
      return editor;
    }
  }

Das Problem:
Eigentlich funktioniert es so halb, aber erst wenn ich das zweite mal in die Zelle Klicke, Sprich:
Eingabe wird unkorrigiert ausgegeben. Erst beim zweiten mal reinklicken sehe ich die Korrektur im Editor.
Ich vermute, das geht nicht, weil anfangs die Zelle noch leer ist, da kann man ja noch nichts korrigieren. Weiter vermute ich dass ich eventuell den falschen Ansatz habe mit dem CellEditor, weil der darin stehende code offenbar nur wirksam wird, wenn der Zelleninhalt in den Editor übernommen wird, und nicht wenn der Wert vom Editor wieder zurückgegeben wird.
 
Ich würde wahrscheinlich eher ein eigenes TableModel implementieren. Sowas in die Richtung:
Java:
public class HexTableModel extends AbstractTableModel {

    private String[] columnNames;
    private Object[][] data;

    public HexTableModel(String[] columnNames, Object[][] data) {
        this.columnNames = columnNames;
        this.data = data;
    }

    private String checkHexString(String value) {
        return "0x" + value;
    }

    @Override
    public int getRowCount() {
        return data.length;
    }

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

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

    @Override
    public void setValueAt(Object value, int row, int col) {
        if (col == 1) {
           data[row][col] = checkHexString(String.valueOf(value));
        } else {
            data[row][col] = value;
        }
        fireTableCellUpdated(row, col);
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        return data[rowIndex][columnIndex];
    }

    @Override
    public boolean isCellEditable(int row, int col) {
        return true;
    }
}
 
Hallo Robat,
danke für Deine Antwort. Das Funktioniert mit deinem Ansatz.

Nun hab ich aber zwei neue Probleme:
1) ich hab in einer Kolonne Checkboxes welche ich mit der Methode getColumnClass realisiert hab:
Java:
        public Class getColumnClass(int c) {
          if (c < getColumnCount()-1)  
            return String.class;
          else
            return Boolean.class;
        }
(Wenn diese Methode einen boolean zurückliefert, wird eine Checkbox in die Zelle eingefügt)
Diese funktioniert aber offenbar im neuen Model nicht.
2) Im DefaultTableModel gibt es eine Methode addRow() und eine removeRow() welche im neuen Model auch nicht existieren. Da gibt es ja einfach dieses Array of Array [][] und dieses kann ich wohl nicht einfach so erweitern oder reduzieren, ohne ein neues Array zu machen und alles komplett umzukopieren?

Eigentlich möchte ich das DefaultTableModel behalten und die Methode setValueAt wie du sie implementiert hast dort einbauen. Nur komme ich nicht an die Daten ran, denn obwohl die Klasse als
Java:
public DefaultTableModel(Object[][] data,
                         Object[] columnNames)
deklariert ist, kann ich auf "data" nicht zugreifen? und setValueAt kann ich ja auch nicht benutzen da ich diese ja überschreiben will (würde rekursiv)
Vielleicht weiss jemand ob ich an diese Daten rankomme?
 
Diese funktioniert aber offenbar im neuen Model nicht.
Kann ich so nicht nachvollziehen. Mit dem überarbeiteten Model (stell ich ganz am Ende noch mal rein) funktionieren auch die Checkboxen wunderbar. (Kenne natürlich deine Anforderungen nicht).

Eine Methode um eine Zeile zu löschen oder hinzuzufügen kannst du auch in dem Model implementieren. Dazu würde ich von einem Object[][][iCode] zu einer [iCode]List<Object[]> wechseln, dann geht das ganze etwas einfacher. Zstl. könnte man aus dem Object[] sich noch eine eigene Klasse basteln, das hab ich jetzt hier aber mal weggelassen.
Am Ende könnte die Benutzung dann so aussehen:
Java:
HexTableModel model = new HexTableModel(new String[]{"Spalte1", "Spalte2", ..., "Booleanwert"});
JTable table = new JTable(model);
..
add(table);

buttonToAddRow.addActionListener(e -> {
    HexTableModel model = (HexTableModel) table.getModel();
    model.addRow(new Object[]{"10", "A", Boolean.TRUE});
});
buttonToRemoveRow.addActionListener(e -> {
    HexTableModel model = (HexTableModel) table.getModel();
    model.removeRow(1); //oder
    model.removeRow(object); // wobei object sowas sein sollte new Object[]{ "..", "...", true/false}
}
Das endgültige Model könnte dann so aussehen
Java:
public class HexTableModel extends AbstractTableModel {

    private String[] columnNames;
    private List<Object[]> data;

    public HexTableModel(String[] columnNames, List<Object[]> data) {
        this.columnNames = columnNames;
        this.data = data;
    }

    public HexTableModel(String[] columnNames) {
        this(columnNames, new ArrayList<>());
    }

    private String checkHexString(String value) {
        return "0x" + value;
    }

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

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

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

    @Override
    public Class<?> getColumnClass(int columnIndex) {
        return columnIndex < getColumnCount() - 1 ? String.class : Boolean.class;
    }

    @Override
    public void setValueAt(Object value, int row, int col) {
        Object[] rowToBeSet = data.get(row);
        if (col == 1) {
            rowToBeSet[col] = checkHexString(String.valueOf(value));
        } else {
            rowToBeSet[col] = value;
        }
        data.set(row, rowToBeSet);
        fireTableCellUpdated(row, col);
    }

    public void addRow(Object[] row) {
        data.add(row);
        int index = data.indexOf(row);
        fireTableRowsInserted(index, index);
    }

    public void removeRow(int index) {
        if (index >= 0 && index < data.size()) {
            data.remove(index);
            fireTableRowsDeleted(index, index);
        }
    }

    public void removeRow(Object[] row) {
        removeRow(data.indexOf(row));
    }

    @Override
    public Object getValueAt(int rowIndex, int columnIndex) {
        return data.get(rowIndex)[columnIndex];
    }

    @Override
    public boolean isCellEditable(int row, int col) {
        return true;
    }
}
 

Zurück
Oben