Mehrzeiligen JTableHeader programmieren

Ich habe mal mit mehrzeiligen Headern experimentiert. Ein ähnliches Beispiel habe ich im Netz gefunden. Ich habs erweitert bzw. umgebaut und möchte, dass die Header-Zellen, die ein Sa und So enthalten gefärbt werden.
Nur warum wird hier der gesamte Header gefärbt?

import java.text.*;
import java.util.*;
import javax.swing.*;
import javax.swing.table.*;

public class MainFrame extends JFrame {
   public MainFrame(String title) {
      super(title);
      setSize(1024, 768);
      setLocationRelativeTo(null);
      setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
      
      DateFormat df = new SimpleDateFormat("EEE");
      Calendar cal = Calendar.getInstance();
      
      String[] columnNames = new String[31];
      int i = 0, j = 0;
      for(i = 0, j = cal.getActualMaximum(Calendar.DAY_OF_MONTH); i < j; i++) {
         cal.set(Calendar.DAY_OF_MONTH, i+1);
         columnNames** = df.format(cal.getTime())+"
"+String.valueOf(i+1);
      }
      if(i < columnNames.length) { //restliche Felder leer "befüllen"
         for(int empty = i; empty < columnNames.length; empty++) {
            columnNames[empty] = "";
         }
      }

      DefaultTableModel model = new DefaultTableModel(columnNames, 1);
      JTable table = new JTable(model);
      table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
      MultiLineHeaderRenderer renderer = new MultiLineHeaderRenderer();
      Enumeration enu = table.getColumnModel().getColumns(); //Spalten besorgen
      while(enu.hasMoreElements()) {
         ((TableColumn)enu.nextElement()).setHeaderRenderer(renderer); //Renderer für Spalte setzen
      }      
      
      TableColumnModel cm = table.getColumnModel();
      for(i = 0, j = cm.getColumnCount(); i < j; i++) {
         cm.getColumn(i).setPreferredWidth(30);
      }
      
      add(new JScrollPane(table));
   } 

   class MultiLineHeaderRenderer extends JList implements TableCellRenderer {
      public MultiLineHeaderRenderer() {
         setOpaque(true);
         setForeground(UIManager.getColor("TableHeader.foreground"));
         setBackground(UIManager.getColor("TableHeader.background"));
         setBorder(UIManager.getBorder("TableHeader.cellBorder"));
         ListCellRenderer renderer = getCellRenderer();
         ((JLabel)renderer).setHorizontalAlignment(JLabel.CENTER);
         setCellRenderer(renderer);
      }

      public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
         setFont(table.getFont());

         String str = (value == null) ? "" : value.toString();
         
         if(str.startsWith("Sa") || str.startsWith("So")) {
            setBackground(Color.PINK);
         }
         
         java.io.BufferedReader br = new java.io.BufferedReader(new java.io.StringReader(str));
         String line;
         Vector<String> v = new Vector<String>();
         try {
            while ((line = br.readLine()) != null) {
               v.add(line);
            }
         } catch (java.io.IOException ex) {
            ex.printStackTrace();
         }
         setListData(v);
         return this;
      }
   }
}```

Ein ähnliches Problem hatte ich mal mit einem tree, ich wollte immer ein „geöffneten“ Knoten im tree Bold setzen, aber es hat entweder immer alles fett gemacht oder alles nach dem „geöffnetem“ Knoten. Hab es dann so gelöst das ich quasi nach dem bold setzen des Knotens die anderen Knoten auf den Standard-Wert gesetzt. Bei Dir würde es dann wie folgt ausschauen:

private final Color m_defaultBackgroundColor = UIManager.getColor("TableHeader.background"); // je nach belieben auch static, name dann uppercase :)
//...
if(str.startsWith("Sa") || str.startsWith("So"))
    setBackground(Color.PINK);
else
    setBackground(m_defaultBackgroundColor);

Vielleicht gibt es noch einen elleganteren Weg, würde mich auch interessieren aber vielleicht ist das auch der gängige, ich mach es jedenfalls immer so :slight_smile:

Gut Schuß
VuuRWerK :wink:

Ich bin platt, darauf wäre ich im Leben nicht gekommen, das mal so zu probieren! Danke! :slight_smile:
Aber warum wird die Bedingung ausgeführt, obwohl sie gar nicht gegeben ist?

Beni? André? Ihr seid doch JTable-Spezis, habt ihr da 'ne Antwort?

Na was heist ausgeführt, es wird eher die Frabe im Objekt gespeichert und nicht wieder zurückgesetzt, was man eben dann händisch machen muss. Darauf gekommen es mal so zuprobieren bin ich damals beim debuggen, als ich merkte das der gesetzte Font nicht wieder auf Standard Wert stand wie erwartet.

Aber wenn es dennoch eine Erklärung dafür gibt bzw. eine andere lösung würd ich mich auch freuen mehr darüber zu erfahren, ich denke Beni und Andre sind genau die richtigen die das wissen :wink:

Gut Schuß
VuuRWerK :wink:

Das wäre eine Erklärung. Ich war bis jetzt immer der Meinung, es würde jeweils ein neues Objekt erzeugt und zurückgegeben.

Genau das dachte ich ja auch immer :slight_smile:

Gut Schuß
VuuRWerK :wink:

nein, ihr setzt den Renderer einmal und erzeugt da ja ein neues Objekt, und dieses wird immer für diesen Zweck verwendet.
Du kannst in deinem Renderer jedes mal ein neues Objekt erzeugen, das du “malst” dann wird es immer zurückgesetzt

Ich glaub ich hab den Unterschied:

Wenn man eine Component erstellt die den CellRenderer implementiert, muss man es wie folgt machen:

class DerivedJList extends JList implements ListCellRenderer
{
    public DerivedJList() {
        setCellRenderer(this);
    }

    @Override
    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
        if(value instanceof Irgendwas)
            setBackground(Color.PINK);
        else
            setBackground(Color.WHITE);

        return(this);
    }
}

Denn man verändert das eigene Object(this).

Wenn man aber einen eigenen Renderer dafür macht kann man es so machen:

class DerivedJListRenderer extends DefaultListCellRenderer// extends JLabel
{
    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
        Component comp = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
        if(value instanceof Irgendwas)
            comp.setBackground(Color.PINK);

        return(comp);
    }
}

Da man dann nur die aktuelle Zeile von der Liste bekommt und damit auch bearbeitet. So wird nicht für diese Zeit die komplette Liste gefärbt sondern nur die Zeile von interesse. Ich glaub so könnte es sein, denn genau diese 2 Ansätze habe ich schonmal gemacht :slight_smile: Gerade eben vor einer Stunde erst den 2. Ansatz ^^

Gut Schuß
VuuRWerK :wink:

Eine Zelle ist eigentlich nur ein “Stempelabdruck”.
Der Renderer ist ein einziger konfigurierbarer Stempel, der alle Zellen mit gleicher Datenart stempelt.
Wenn die Konfiguration nicht für alle diese Zellen gleich ist, dann müssen natürlich alle Konfigurationsfälle vorgesehen werden.
Im vorliegenden Fall muss der Stempel also wissen, welchen Hintergund er fürs Wochende setzen soll und welchen für die Arbeitstage.
Ich mach’s gewöhnlich so, daß ich in der Methode “getTableCellRendererComponent” zuerst die variablen Werte (hier der Hintergrund) mit den Standardwerten initialisiere.
Danach kann für besondere Fälle ein anderer Wert gesetzt werden.
Siehe auch: Concepts: Editors and Renderers

[QUOTE=VuuRWerK]Ich glaub ich hab den Unterschied:
Wenn man aber einen eigenen Renderer dafür macht kann man es so machen:

class DerivedJListRenderer extends DefaultListCellRenderer// extends JLabel
{
    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
        Component comp = super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
        if(value instanceof Irgendwas)
            comp.setBackground(Color.PINK);

        return(comp);
    }
}
```[/QUOTE]

Der DefaultRenderer verwendet immer this, damit musst du den Rückgabewert nicht auffangen und bearbeiten sondern kannst auch einfach this benutzen

Du hast Recht, hatte es nur eben vorhin genauso gemacht auf Arbeit, deswegen einfach Copy-Paste :slight_smile:

Gut Schuß
VuuRWerK :wink: