Anzeigefehler im JFrame (GridBagLayout), wenn JTabelle im JScrollPane länger wird

Davicito

Erfahrenes Mitglied
Hallo liebes Forum,

ich habe ein Multithread-Programm geschrieben, welches Datensätze (zeilenweise) in eine Tabelle füllt.
Leider wird nach der Befüllung, die Darstellung in meinem Fenster verschoben (s. 2. Bild, unten) Ich habe leider gerade kein Plan wieso das geschied.
Ich denke mir das es entweder am GridBagLayout oder an der JScrollPane liegen müsste. Wäre schön wenn mir dazu jemand einen Rat beisteuern könnte, da ich im Internet nicht ganz fündig geworden bin.
Sehr vielen Dank im Voraus!

Ich habe folgene Programmansicht:

Unbenannt.jpg

Java:
...
private int frameSizeX;
private int frameSizeY;

JFrame mainF;
Container frameC;
GridBagLayout gbl;
private JTable tableT;

// Konstruktor
public View(){
  //Window (Frame) size
  frameSizeX = 1100;
  frameSizeY = 500;

  mainF = new JFrame();
  frameC = new Container();
  gbl = new GridBagLayout();

  myTableModel = new MyTableModel();
  tableT = new JTable(myTableModel){

  tableT.getModel(); 
  tableT.setFont(new Font("Arial", Font.PLAIN, 13));

  // Colum sizing
  TableColumn column = null;
  for (int i = 0; i < tableT.getColumnCount(); i++) {
    column = tableT.getColumnModel().getColumn(i);
    switch(i){
      case 0:  column.setPreferredWidth(30); break;
      case 1:  column.setPreferredWidth(45); break;
      case 4:  column.setPreferredWidth(70); break;
      case 6:  column.setPreferredWidth(45); break;
      default: column.setPreferredWidth(120); break;
    } 
  }

  tableT.setPreferredScrollableViewportSize(new Dimension(frameSizeX,450)); 
  tableT.setFillsViewportHeight(true); 
 
  scrollP = new JScrollPane(tableT);
}

public void frame(){
  mainF.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
  mainF.setSize(frameSizeX, frameSizeY);         // Fenstergröße
  mainF.setLocationRelativeTo(null);                 // Bildschirmmitte

  frameC = mainF.getContentPane();
  frameC.setLayout(gbl);                                   // füge dem Container das GridBagLayout hinzu

  // Die Mathode setFrameElements setzt nach dem GridBagLayout alle Buttons- und JLabel-Elemente
  // sowie das JScrollPane-Element in das JFrame
  setFrameElements(sourceL,  0, 0, 1, 1, 0.0, 0.0 );
  setFrameElements(filePathTF,  0, 1, 2, 1, 0.6, 0.0 ); 
  setFrameElements(statusLable, 0, 2, 1, 1, 0.001, 0.0 );
  setFrameElements(status,  1, 2, 1, 1, 0.8, 0.0 );
  setFrameElements(openB,  2, 1, 1, 1, 0.4, 0.0 );
  setFrameElements(scanB,  2, 2, 1, 1, 0.001, 0.0 );
  setFrameElements(exportB,  2, 3, 1, 1, 0.0, 0.0 ); 
  setFrameElements(scrollP,  0, 4, 0, 1, 0.0, 0.0 );

  // Fenster wird gezeichnet/aufgerufen
  mainF.setResizable(true);
  mainF.pack();
  mainF.setVisible(true);
}


private void setFrameElements( Component c, int x, int y, int width, int height,
                                                     double weightx, double weighty)
{
  GridBagConstraints gbc = new GridBagConstraints();

  gbc.fill = GridBagConstraints.HORIZONTAL;
  gbc.anchor = GridBagConstraints.LAST_LINE_START;
  gbc.gridx = x;
  gbc.gridy = y;
  gbc.gridwidth = width;
  gbc.gridheight = height;
  gbc.weightx = weightx;
  gbc.weighty = weighty;

  gbc.insets.set(3, 6, 3, 6);
  gbl.setConstraints(c, gbc);
  frameC.add(c);
}

Das Problem was ich habe ist, wenn die Tabelle befüllt wird (zeilenweise aus der Model) und länger wird als die Ansicht hergibt, wird zunächst von der JScrollPane der Scrollbalken eingebländet und wenn die Befüllung abgeschlossen ist, kommt es zu einem Clipping-Effekt, wie unten im Bild dargestellt ist.

Unbenannt2.jpg

Vielleicht ist noch hilfreich zu wissen, von wo aus ich meine Table (MyTableModel) aktualisiere (s. nachfolgenden Code: Zeilen 40, 41)

AktualisierungsThread aus meiner Controller-Klasse
Java:
...
public class Controler implements ActionListener{
  private Model m = null;
  private View  v = null;

  public Controler(View v, Model m){
    this.m = m;
    this.v = v;
  
    //Objekte zum Dateien auswählen
    this.fileOpen = new JFileChooser();
    this.fileExport = new JFileChooser();
  
    //Schalte das Auswählen mehrere Dateien ein.
    this.fileOpen.setMultiSelectionEnabled(true);
    ...
  }

  @Override
  public void actionPerformed(ActionEvent ae){  
    if(ae.getSource() == v.getOpenB()){
      ...    
    }  
    else if(ae.getSource() == v.getScanB()){ 
    // File-Übergabe an die Model
    m.setFiles(fileOpen.getSelectedFiles());  // ermöglicht Multithead-Abarbeitung
   
    // Die Model überprüft die Dateien nach personenbezogene Daten
    m.readDatasets();
   
    // Thread zur
   Thread t = new Thread(new Runnable() {
      @Override
      public void run() {
         v.setStatus("");
         v.setStatus("Datenscan");
        
         while(m.getThreadStatus() != Thread.State.TERMINATED){
            // Aktualisiere die Tabelle (Tablemodel)
            v.getMyTableModel().setTableContent(m.getReadBuffer());
            v.getMyTableModel().update();  //Tabelle wird in der GUI aktualisiert
  
            try {
                Thread.sleep(100);
            } catch (InterruptedException ex) {
                Logger.getLogger(Controler.class.getName()).log(Level.SEVERE, null, ex);
            }  
         }
  
        //Wenn alle Threads in der Filethread-Klasse beendet sind, soll der Benutzer über den Scannvorgang
        //informiert werden und der Button für den Export als CSV-Datei aktiviert werden.
        if(m.getThreadStatus() == Thread.State.TERMINATED){
           v.setStatus("");
           v.setStatus("Scan beendet");
           v.getExportB().setEnabled(true);
        }  
     }
   });  
   t.start();  
}
else if(ae.getSource() == v.getExportB()){  
  
   fileExport.setDialogType(JFileChooser.SAVE_DIALOG);
   int request = fileExport.showSaveDialog(null);
   ...
   v.setStatus("");
   v.setStatus("Export durchgeführt");
  }
  else System.out.println("Es wurde kein CSV-Datei mit Inhalt generiert!");  
}// Ende der Methode actionPerformed() 
}// Klassenende
 
Zuletzt bearbeitet:
Ergänzung von oben

Wenn ich in dem GridBagLayout (durch die Methode setFrameElements(), erster Code-Beispiel, Zeile 58, oben) das Status-Element "status" (JLabel) auskommentiere, dann habe ich den komischen Effekt nicht (2. Bild, oben).
Nur wenn ich das Status-Element wieder rein nehme, und die Tabelle länger wird als ich im JScrollPane sehen kann, wird durch die Statusänderung - in der Controller-Klasse (Code-Zeilen: 35, 36, 53,54, 66 u. 67 , oben) - dieser Effekt hervorgerufen. Anders ausgedrückt, wenn ich mir weniger Datensätze ausgeben lasse (also passend in der Benutzeransicht, ScrollPane) sieht alles ganz ordendlich aus (s. Bild unten)

Bild listet personenbezogene Daten auf und wurden aus Datenschutzgründen unkenntlich gemacht
Unbenannt3.jpg
 
Zuletzt bearbeitet:
...nun ich habe na langem Suchen herausgefunden, das es mit dem horizontalen Scroll-Balken zutun hat.
Wenn die JTable in dem JScrollPane gesetzt wird, war vorher der horizantale Scroll-Balken nicht aktiv. Das hat die JScrollPane automatisch gehändelt, sobald dieser erforderlich war (also die Tabelle länger als der Ausschnitt der JScollPane war).
Da der horizontale Scroll-Balken natürlich auch einige Pixel Platz, in der ScrollPane, in Anspruch genommen hat, wurde die Tabelle zusammengestaucht und nur durch das Setzen des Status "Scan beendet" musst die GUI natürlich neu aktualisiert werden (vom AktualisierungsThread), welches dann diesen unschönen Effekt hervorgerufen hat.

Deshalb habe ich einmal angenommen, wenn ich den horizontalen Scroll-Balken statisch in meine JScrollPane setze, ich diesen unliebsamen Effekt vermeiden könnte.
Also habe ich folgende simple Code-Zeile in meine View-Klasse ergäntzt (s. View Klasse, unterhalb der Zeile 42) Damit hat sich mein Problem in Rauch aufgelößt :D

Java:
...
scrollP.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
....

Aktueller Ausschnitt aus dem ersten Code-Baispiel von oben, nun mit der Änderung
Java:
...
  // Colum sizing
  TableColumn column = null;
  for (int i = 0; i < tableT.getColumnCount(); i++) {
     column = tableT.getColumnModel().getColumn(i);
     switch(i){
       case 0:  column.setPreferredWidth(30); break;
       case 1:  column.setPreferredWidth(45); break;
       case 4:  column.setPreferredWidth(70); break;
       case 6:  column.setPreferredWidth(45); break;
       default: column.setPreferredWidth(120); break;
    }
  }

  tableT.setPreferredScrollableViewportSize(new Dimension(frameSizeX,450));
  tableT.setFillsViewportHeight(true);

  scrollP = new JScrollPane(tableT);
  //Änderung zur Vermeidung eines unschönen GUI-Effekts!
  scrollP.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
}
Lösung zu diesem Problem, gefunden auf: http://www2.htw-dresden.de/~beck/JAVA11/SWING/scrolling.html

Liebe Grüße!
 
Zuletzt bearbeitet:
Zurück