Problem mit TableModel für Tabelle mit fixierten Spalten

Hallo, ich habe ein Problem bei der Erzeugung eines TableModels.
Ich habe mir für eine Tabelle mit fixierten Spalten etwas Code aus dem Netz gezogen. Der sieht (leicht verändert) so aus:

import javax.swing.*;
import javax.swing.table.*;
import javax.swing.event.*;

public class FixedTableDemo extends JFrame {
   private Object[][] data;
   private Object[] column;
   private JTable fixedTable, table;
   
   public FixedTableDemo(String title) {
      super(title);
      setSize(400, 200);
      setLocationRelativeTo(null);
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      
      data =  new Object[][]{
         {"1","11","A","","","","",""},
         {"2","22","","B","","","",""},
         {"3","33","","","C","","",""},
         {"4","44","","","","D","",""},
         {"5","55","","","","","E",""},
         {"6","66","","","","","","F"},
         {"7","77","G","","","","",""},
         {"8","88","","H","","","",""},
         {"9","99","","","I","","",""},
         {"0","00","","","","J","",""}};
         
      column = new Object[]{"fixed 1","fixed 2","a","b","c","d","e","f"};

      AbstractTableModel fixedModel = new AbstractTableModel() {
         public int getColumnCount() {
            return 2;
         }
         
         public int getRowCount() {
            return data.length;
         }
         
         public String getColumnName(int col) {
            return (String)column[col];
         }
         
         public Object getValueAt(int row, int col) {
            return data[row][col];
         }
      };
      
      AbstractTableModel    model = new AbstractTableModel() {
         public int getColumnCount() {
            return column.length -2;
         }
         
         public int getRowCount() {
            return data.length;
         }
         
         public String getColumnName(int col) {
          return (String)column[col +2];
         }
         
         public Object getValueAt(int row, int col) {
           return data[row][col+2];
         }
         
         public void setValueAt(Object obj, int row, int col) {
           data[row][col +2] = obj;
         }
         
         public boolean CellEditable(int row, int col) {
           return true;
         }
      };

      fixedTable = new JTable( fixedModel ) {
         public void valueChanged(ListSelectionEvent e) {
            super.valueChanged(e);
            checkSelection(true);
         }
      };
      
      table = new JTable( model ) {
         public void valueChanged(ListSelectionEvent e) {
            super.valueChanged(e);
            checkSelection(false);
         }
      };
      
      fixedTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
      table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
      fixedTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
      table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

      JScrollPane scroll = new JScrollPane( table );
      JViewport viewport = new JViewport();
      viewport.setView(fixedTable);
      viewport.setPreferredSize(fixedTable.getPreferredSize());
      scroll.setRowHeaderView(viewport);
      scroll.setCorner(JScrollPane.UPPER_LEFT_CORNER,fixedTable.getTableHeader());

      add(scroll, BorderLayout.CENTER);
   }

   private void checkSelection(boolean isFixedTable) {
      int fixedSelectedIndex = fixedTable.getSelectedRow();
      int      selectedIndex = table.getSelectedRow();
      if (fixedSelectedIndex != selectedIndex) {
         if (isFixedTable) {
            table.setRowSelectionInterval(fixedSelectedIndex,fixedSelectedIndex);
         }
         else {
            fixedTable.setRowSelectionInterval(selectedIndex,selectedIndex);
         }
      }
   }
   
   public static void main(String[] args) {
      new FixedTableDemo("FixedTableDemo").setVisible(true);
   }
}```
Der Code funktioniert so auch ganz gut.


Der nächste Schritt war ein AbstractTableModel zu verwenden, in dem statt Arrays Vectoren verwendet werden. Der Code dazu sieht so aus:
```import java.util.*;
import java.awt.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.event.*;

public class FixedTableDemo2 extends JFrame {
   private Vector data;
   private Vector column;
   private JTable fixedTable, table;

   public FixedTableDemo2(String title) {
      super(title);
      setSize(400, 200);
      setLocationRelativeTo(null);
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

      data = new Vector();
      fill(data); //Vector mit Daten füllen

      column = new Vector();
      fillColumnNames(column);

      AbstractTableModel fixedModel = new AbstractTableModel() {
         public int getColumnCount() {
            return 2;
         }

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

         public String getColumnName(int col) {
            return (String)column.get(col);
         }

         public Object getValueAt(int row, int col) {
            return ((Vector)data.get(row)).get(col);
         }
      };

      AbstractTableModel    model = new AbstractTableModel() {
         public int getColumnCount() {
            return column.size() -2;
         }

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

         public String getColumnName(int col) {
            return (String)column.get(col +2);
         }

         public Object getValueAt(int row, int col) {
            return ((Vector)data.get(row)).get(col +2);
         }

         public void setValueAt(Object obj, int row, int col) {
            ((Vector)data.get(row)).set(col +2, obj);
         }

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

      fixedTable = new JTable( fixedModel ) {
         public void valueChanged(ListSelectionEvent e) {
            super.valueChanged(e);
            checkSelection(true);
         }
      };

      table = new JTable( model ) {
         public void valueChanged(ListSelectionEvent e) {
            super.valueChanged(e);
            checkSelection(false);
         }
      };

      fixedTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
      table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
      fixedTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
      table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

      JScrollPane scroll = new JScrollPane( table );
      JViewport viewport = new JViewport();
      viewport.setView(fixedTable);
      viewport.setPreferredSize(fixedTable.getPreferredSize());
      scroll.setRowHeaderView(viewport);
      scroll.setCorner(JScrollPane.UPPER_LEFT_CORNER,fixedTable.getTableHeader());

      add(scroll, BorderLayout.CENTER);
   }

   private void checkSelection(boolean isFixedTable) {
      int fixedSelectedIndex = fixedTable.getSelectedRow();
      int      selectedIndex = table.getSelectedRow();
      if (fixedSelectedIndex != selectedIndex) {
         if (isFixedTable) {
            table.setRowSelectionInterval(fixedSelectedIndex,fixedSelectedIndex);
         }
         else {
            fixedTable.setRowSelectionInterval(selectedIndex,selectedIndex);
         }
      }
   }
   
   private void fill(Vector data) {
      Vector v = new Vector();
      v.add("1");
      v.add("11");
      v.add("A");
      v = fillEmpty(v, 5);
      data.add(v);

      v = new Vector();
      v.add("2");
      v.add("22");
      v.add("");
      v.add("B");
      v = fillEmpty(v, 4);
      data.add(v);
      
      v = new Vector();
      v.add("3");
      v.add("33");
      v = fillEmpty(v, 2);
      v.add("C");
      v = fillEmpty(v, 3);
      data.add(v);
      
      v = new Vector();
      v.add("4");
      v.add("44");
      v = fillEmpty(v, 3);
      v.add("D");
      v = fillEmpty(v, 2);
      data.add(v);
      
      v = new Vector();
      v.add("5");
      v.add("55");
      v = fillEmpty(v, 4);
      v.add("E");
      v.add("");
      data.add(v);
      
      v = new Vector();
      v.add("6");
      v.add("66");
      v = fillEmpty(v, 5);
      v.add("F");
      data.add(v);

      v = new Vector();
      v.add("7");
      v.add("77");
      v.add("G");
      v = fillEmpty(v, 5);
      data.add(v);
      
      v = new Vector();
      v.add("8");
      v.add("88");
      v.add("");
      v.add("H");
      v = fillEmpty(v, 4);
      data.add(v);
      
      v = new Vector();
      v.add("9");
      v.add("99");
      v = fillEmpty(v, 2);
      v.add("I");
      v = fillEmpty(v, 3);
      data.add(v);
      
      v = new Vector();
      v.add("0");
      v.add("00");
      v = fillEmpty(v, 3);
      v.add("J");
      v = fillEmpty(v, 2);
      data.add(v);

   }
   
   private Vector fillEmpty(Vector v, int i) {
      for(int x = 0; x < i; x++) {
         v.add("");
      }
      
      return v;
   }
   
   private void fillColumnNames(Vector v) {
      v.add("fixed 1");
      v.add("fixed 2");
      v.add("a");
      v.add("b");
      v.add("c");
      v.add("d");
      v.add("e");
      v.add("f");
   }

   public static void main(String[] args) {
      new FixedTableDemo2("FixedTableDemo2").setVisible(true);
   }
}```

Funktioniert auch wie es soll.


Beim nächsten Schritt steige ich immer wieder aus, weil ich Probleme beim horizontalen Scrollen bekomme (ArrayIndexOutOfBoundsException) und auch der Inhalt der rechten Tabelle fehlt.
Dieses Mal verwende ich aber ein DefaultTableModel für beide Tabellen.

```import java.awt.*;
import java.util.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.event.*;

public class FixedTableDemo3 extends JFrame {
   private Vector data;
   private Vector column;
   private JTable fixedTable, table;

   public FixedTableDemo3(String title) {
      super(title);
      setSize(400, 200);
      setLocationRelativeTo(null);
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

      data = new Vector();
      fill(data); //Vector mit Daten füllen

      column = new Vector();
      fillColumnNames(column);

      DefaultTableModel fixedModel = new DefaultTableModel(data, column) {
         public int getColumnCount() {
            return 2;
         }

         public int getRowCount() {
            return dataVector.size();
         }

         public String getColumnName(int col) {
            return (String)columnIdentifiers.get(col);
         }

         public Object getValueAt(int row, int col) {
            return ((Vector)dataVector.get(row)).get(col);
         }
      };

      DefaultTableModel model = new DefaultTableModel(data, column) {
         public int getColumnCount() {
            return columnIdentifiers.size() -2;
         }

         public int getRowCount() {
            return dataVector.size();
         }

         public String getColumnName(int col) {
            return (String)columnIdentifiers.get(col +2);
         }

         public Object getValueAt(int row, int col) {
            return ((Vector)dataVector.get(row)).get(col +2);
         }

         public void setValueAt(Object obj, int row, int col) {
            ((Vector)dataVector.get(row)).set(col +2, obj);
         }
      };

      fixedTable = new JTable( fixedModel ) {
         public void valueChanged(ListSelectionEvent e) {
            super.valueChanged(e);
            checkSelection(true);
         }
      };

      table = new JTable( model ) {
         public void valueChanged(ListSelectionEvent e) {
            super.valueChanged(e);
            checkSelection(false);
         }
      };

      fixedTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
      table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
      fixedTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
      table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

      JScrollPane scroll = new JScrollPane( table );
      JViewport viewport = new JViewport();
      viewport.setView(fixedTable);
      viewport.setPreferredSize(fixedTable.getPreferredSize());
      scroll.setRowHeaderView(viewport);
      scroll.setCorner(JScrollPane.UPPER_LEFT_CORNER,fixedTable.getTableHeader());

      add(scroll, BorderLayout.CENTER);
   }

   private void checkSelection(boolean isFixedTable) {
      int fixedSelectedIndex = fixedTable.getSelectedRow();
      int      selectedIndex = table.getSelectedRow();
      if (fixedSelectedIndex != selectedIndex) {
         if (isFixedTable) {
            table.setRowSelectionInterval(fixedSelectedIndex,fixedSelectedIndex);
         }
         else {
            fixedTable.setRowSelectionInterval(selectedIndex,selectedIndex);
         }
      }
   }

   private void fill(Vector data) {
      Vector v = new Vector();
      v.add("1");
      v.add("11");
      v.add("A");
      v = fillEmpty(v, 5);
      data.add(v);

      v = new Vector();
      v.add("2");
      v.add("22");
      v.add("");
      v.add("B");
      v = fillEmpty(v, 4);
      data.add(v);

      v = new Vector();
      v.add("3");
      v.add("33");
      v = fillEmpty(v, 2);
      v.add("C");
      v = fillEmpty(v, 3);
      data.add(v);

      v = new Vector();
      v.add("4");
      v.add("44");
      v = fillEmpty(v, 3);
      v.add("D");
      v = fillEmpty(v, 2);
      data.add(v);

      v = new Vector();
      v.add("5");
      v.add("55");
      v = fillEmpty(v, 4);
      v.add("E");
      v.add("");
      data.add(v);

      v = new Vector();
      v.add("6");
      v.add("66");
      v = fillEmpty(v, 5);
      v.add("F");
      data.add(v);

      v = new Vector();
      v.add("7");
      v.add("77");
      v.add("G");
      v = fillEmpty(v, 5);
      data.add(v);

      v = new Vector();
      v.add("8");
      v.add("88");
      v.add("");
      v.add("H");
      v = fillEmpty(v, 4);
      data.add(v);

      v = new Vector();
      v.add("9");
      v.add("99");
      v = fillEmpty(v, 2);
      v.add("I");
      v = fillEmpty(v, 3);
      data.add(v);

      v = new Vector();
      v.add("0");
      v.add("00");
      v = fillEmpty(v, 3);
      v.add("J");
      v = fillEmpty(v, 2);
      data.add(v);

   }

   private Vector fillEmpty(Vector v, int i) {
      for(int x = 0; x < i; x++) {
         v.add("");
      }

      return v;
   }

   private void fillColumnNames(Vector v) {
      v.add("fixed 1");
      v.add("fixed 2");
      v.add("a");
      v.add("b");
      v.add("c");
      v.add("d");
      v.add("e");
      v.add("f");
   }

   public static void main(String[] args) {
      new FixedTableDemo3("FixedTableDemo3").setVisible(true);
   }
}```

Der nächste Schritt wäre dann der Einbau eines von DefaultTableModel abgeleiteten TableModels.
Aber ich finde hier erst mal den Fehler nicht, der für beide Probleme verantwortlich ist. Kann mir da mal jemand helfen? Irgendwas übersehe ich hier. Nur was?

wo bekommst du den Fehler?
Was ich mich bisschen wundere ist warum du bei deinen Spalten immer +2 machst.

:wink: Das hat schon seinen Grund, Eagle. Denn so kann man eine Art RowHeader auf mehrere Spalten einer Tabelle abbilden.

Wie du gesehen hast, sind zwei Tabellen auf einer JScrollPane.
Damit nicht beide Tabellen den gleichen Inhalt haben, muss ich ihnen vorgeben, welchen Teil des Models sie anzeigen sollen. Das geht mit den Indiezes. Der linken Tabelle begrenze ich die Anzahl der Spalten durch den festen Rückgabewert von 2. Da diese 2 ersten Spalten bereits in der linken Tabelle angezeigt werden und ich diese nicht in der rechten Tabelle wiederholen will, „blende“ ich diese Spalten aus, in dem ich der rechten Tabelle sage, dass die erste Spalte auf Index 0 + 2 sitzt.
In den anderen beiden Beispielen funktioniert das wunderbar, aber warum bekomme ich beim DefaultTabelModel nun die ArrayIndexOutOfBoundsException?

Probiere einfach mal ein Beispiel aus, dann weißt du, was passiert.
Der Fehler im letzten Beispiel tritt auf, wenn man die rechte Tabelle nach rechts scrollt.

deine Daten werden abgeschnitten
weil wen ndu dir das im Debugger ansiehst, stellst du fest dass dein Vector auf einmal nur noch 6 Elemente in der Breite besitzt

genau, in DefaultTableModel gibts

    private void justifyRows(int from, int to) { 
	
// Sometimes the DefaultTableModel is subclassed 
	// instead of the AbstractTableModel by mistake. 
	// Set the number of rows for the case when getRowCount 
	// is overridden. 
	dataVector.setSize(getRowCount()); 

        for (int i = from; i < to; i++) { 
	    if (dataVector.elementAt(i) == null) { 
		dataVector.setElementAt(new Vector(), i); 
	    }
	    ((Vector)dataVector.elementAt(i)).setSize(getColumnCount());
	}
    }

das kürzt die Daten erst auf 2 Columns beim ersten Model,
und füllt sie dann bis zur Länge 6 mit null auf im zweiten Model,
die Ursprungslänge 8 wird aber nicht mehr erreicht

Mist. :frowning: Habt ihr dafür alternative Lösungsvorschläge?

  • Code von DefaultTableModel soweit nötig kopieren, aber diese Methode auslassen

  • nur

    public void setDataVector(Vector dataVector, Vector columnIdentifiers) {
        this.dataVector = nonNullVector(dataVector);
        this.columnIdentifiers = nonNullVector(columnIdentifiers); 
	justifyRows(0, getRowCount()); 
        fireTableStructureChanged();
    }

überschreiben

  • erst nach dem Einfügen die Datenvectoren füllen bzw. wieder auf den Ursprungsstand reparieren,
    beim Neueinfügen weiterer Zeilen gibts aber ähnliche Problem

sach mal, was stellst du denn für Anfängerfragen :wink:

Was JTable angeht, bin ich wirklich ein Anfänger, da mach ich auch keinen Hehl draus.
Die ist für mich ein Mysterium. Aus diesem Grund will ich mich ja auch damit mehr beschäftigen.

Also ich denke, ich werde einfach die Daten wirklich in zwei Models aufteilen, ohne diese Manipulation. Sollte doch auch gehen.

Diese Frage hat mich daran erinnert, dass ich das http://wiki.byte-welt.net/wiki/Mehrere_TableModels_zu_einem_einzigen_zusammenfügen ja schon ewig mal fertig machen wollte :wink: (Hat aber mit deinem Problem glaubich nicht direkt was zu tun…)