# JTable Zeilen einfärben



## may24 (2. Feb 2012)

Hi zusammen, 

Ich habe einen JTable der Zeilenweise (dtm.addRow(data)) mit Daten gefüllt wird. 
Ich lese dazu Daten aus verschiedenen Files ein. Jeweils abwechselnd sollen die Blöcke farblich abgesetzt werden damit man erkennen kann das Block eins aus einer Datei kommt und Block zwei aus einer Anderen. 
D.h. die ersten ... sagen wir mal 20 Zeilen sollen lightgrey sein die nächsten 5 darkgrey die nächsten 10 wieder lightgrey ... und so weiter.

Ich habe mir schon mal einige Beispiele im Internet angesehen, aber die machen zu 98% keinen Sinn und/oder ich bin zu blöd um sie zu verstehen.

Ok, wir basteln uns eine MyTableCellRenderer Klasse und packen da ein neues:

```
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
```
... rein un die Default Klasse zu überschreiben. Und weiter ? ???:L
Die meisten Beispiele beziehen sich auf eine gezielte Änderung von "Object value" aber das ist ja bei mir nicht der Fall.

Außerdem weiß ich nicht wie ich das in meine GUIModel Klasse (die produziert alle Fenster, table, buttons usw.) einbauen soll.

Könnt ihr mir da weiterhelfen ?


----------



## KrokoDiehl (2. Feb 2012)

Du solltest von DefaultTablecellRenderer erben und dann brauchst du eigentlich "nur" eine andere Hintergrundfarbe setzen:


```
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
    Component comp = super.getTableCellRendererComponent(...);
    if (isSelected) {
        return comp;
    }

    if (row < 20) {
        comp.setBackground( Color.lightGray );
    }
    else {
        comp.setBackground( table.getBackground() );    
    }
    return comp;
}
```
... das mal als Anfang.

Ansonsten musst du der JTable ihre Renderer geben. Am einfachstes wäre es anhand des Datentyps:

```
JTable myTable = new JTable( myModel );
myTable.setDefaultRenderer(Object.class, new MyCellRenderer());
```


----------



## may24 (7. Feb 2012)

Hi,
erst mal vielen Dank für die Hilfe. Es funktioniert auch fast ...

Auf "row" kann ich nicht testen da die Anzahl der Zeilen ja immer unterschiedlich sind.
Hinzu kommt das, wenn ich den zweiten (bzw. dritten + vierten ...) Block hinzufüge, einfach Alle Zeilen (und somit alle Zellen) mit der neuen Farbe gefüllt werden. Da soll ja gerade nicht der Fall sein. Die Blöcke sollen ja farblich voneinander abgesetzt sein.

Scheinbar wirkt sich:

```
c.setBackground(Color.lightGray );
```
auf den ganzen JTable aus.

Nachtrag: Um die etsprechenden Zeilen zu ändern benutze ich folgendes:

```
private boolean toggle = false;
	
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
    {
    	Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);

    	System.out.println(toggle);
    	if (toggle) 
    	{
    		c.setBackground(Color.lightGray );	
    	}
    	else
    	{
    		c.setBackground(Color.GRAY );

    	}
    	
        return c;
    }
    
    public void toggleColor()
    {
    	if (toggle)
    	{
    		toggle = false;
    	}
    	else
    	{
    		toggle = true;
    	}
```

Kann man denn die Farbe einer Zelle - bzw. Zeile - nicht "fest" eintragen so daß sie sich nicht mehr abändern läßt.
Praktisch wenn ich ein addRow mache, den Color Parameter für die Zellen übergebe ...


----------



## may24 (8. Feb 2012)

Hm, soweit ich das sagen kann liegt es "höchstwarscheinlich" am DefaultTableModel listener. 
Da das Ganze ja ala MVC aufgebaut ist, und die Daten aus einem separaten Datenobjeckt kommen, wird bei einem update des JTables gnadenlos die Hintergrundfarbe auf alle Zellen gemapped. Aber das muß doch auch anders gehen.

Jemand 'ne Idee ?


----------



## KrokoDiehl (8. Feb 2012)

Die Renderer funktionieren etwas anders. So ein Renderer-Objekt ist für viele Zeilen/Spalten zuständig und du kannst die Farbe nicht einfach hochzählen, weil er "in beliebiger" rendert.

Wenn du also die Farbe anhand der gegebenen Informationen in der getRenderer-Methode nicht auswerten kannst, musst du dir was anderes ausdenken. Natürlich geht es auch, dass du über das Modell eine Information erfragst:

```
Component c = super.get...

// selektionsfarbe beibehalten
if ( ! isSelected) {
    MyTableModel model = (MyTableModel) table.getModel();
    Color color = model.getColorAt( table.convertRowIndexToModel(row) );
    c.setBackground(color);
}
```


----------



## may24 (9. Feb 2012)

Hm, gehe ich recht in der Annahme das das TableModel auch die information über seine einzelnen Zellen hält ?
In diesem Falle wäre es doch besser das TableModel zu überschreiben und die Zellen explizit (und nur einmal) mit Farbinformation zu versehen. Ein repaint (?) des JTables würde dann daran nichts ändern so lange der TableCellRender nicht angesprochen würde.
Nur weiß ich nicht ob/wie das geht  ...


----------



## KrokoDiehl (9. Feb 2012)

> Hm, gehe ich recht in der Annahme das das TableModel auch die information über seine einzelnen Zellen hält ?


Das TableModel ist die Datengrundlage für die gesamte Tabelle. Man kann sagen, dass das Modell alles weiß und auch alles für die Tabelle liefert. Nur für die Darstellung ist das Modell nicht zuständig, daher ist es so eine Sache ob man die Zeilenfarbe ins Modell hängt. Aber gut, wenn es auf andere Art und Weise zu komplex ist, ist das durchaus vertretbar.



> In diesem Falle wäre es doch besser das TableModel zu überschreiben und die Zellen explizit (und nur einmal) mit Farbinformation zu versehen.


Ja, kann man machen. Ich würde dann von AbstractTableModel erben. Wie du die Farbinformation intern speicherst/ermittels ist deine Sache.



> Ein repaint (?) des JTables würde dann daran nichts ändern so lange der TableCellRender nicht angesprochen würde.
> Nur weiß ich nicht ob/wie das geht ...


Verstehe ich nicht ganz ... die JTable (und die Renderer) sind nur zur Darstellung da. Das heißt weiterhin auch, dass wenn sich am Modell etwas ändert, es diese Änderung mitteilen muss (Methoden á la 
	
	
	
	





```
fireTableDataChanged()
```
). Aber dann bekommen das JTable und Renderer mit und du musst nicht manuell neuzeichnen.

Da du bisher selbst keinen Code postest sodass man ggfs. Probleme sehe kann, war ich so gnädig und habe ein Beispiel herausgesucht: Hier hast du Modell mit Farben pro Zeile (eigentliche Daten sind hier vernachlässigt), einen Renderer und auch das Ändern von Zeilenfarben. Ich hoffe es hilft dir weiter die Zusammenhänge zwischen JTable, TableModel und Renderer zu verstehen:

```
public final class ColorLineDemo {

    private static final class ColoredTableModel extends AbstractTableModel {

        private final Color[] lineColors;
        
        public ColoredTableModel() {
            super();
            lineColors = new Color[] {
                    Color.red, Color.green, Color.blue,
                    Color.lightGray, Color.cyan
            };
        }
        
        public void setColorAt(int rowIndex, Color color) {
            lineColors[ rowIndex ] = color;
            //wichtig für Repaint: mitteilen dass sich Zeile geändert hat
            this.fireTableRowsUpdated(rowIndex, rowIndex);
        }
        
        public Color getColorAt(int rowIndex) {
            return lineColors[rowIndex];
        }
        
        @Override
        public Class<?> getColumnClass(int columnIndex) {
            return Integer.class;
        }
        
        @Override
        public int getRowCount() {
            return 5;
        }

        @Override
        public int getColumnCount() {
            return 5;
        }

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            return rowIndex*columnIndex;
        }
        
    } //class ColoredTableModel
    
    private static final class LineRenderer extends DefaultTableCellRenderer {
        @Override
        public Component getTableCellRendererComponent(JTable table,
                Object value, boolean isSelected, boolean hasFocus, int row,
                int column) {
            Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus,
                    row, column);
            
            if ( ! isSelected) {
                ColoredTableModel model = (ColoredTableModel) table.getModel();
                Color color = model.getColorAt( table.convertRowIndexToModel(row) ); //wichtig so
                c.setBackground(color);
            }
            
            return c;
        }
    } //class LineRenderer
    
    
    public static void main(String[] args) {
        final JTable table = new JTable(new ColoredTableModel());
        table.setDefaultRenderer(Integer.class, new LineRenderer());
        
        final JFrame frame = new JFrame("Color Line Demo");
        frame.add(new JScrollPane(table));
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.pack();
        
        SwingUtilities.invokeLater(new Runnable() {
            @Override
            public void run() {
                frame.setVisible(true);   
            }
        });
        
        
        // nach 2 Sek. Farbe in Zeile 2 ändern
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                    ColoredTableModel model = (ColoredTableModel) table.getModel();
                    model.setColorAt(2, Color.pink);
                } 
                catch (InterruptedException e) {
                }      
            }
        }).start();
    }
}
```


----------



## jgh (9. Feb 2012)

kann mir jemand dieses Verhalten, bzw. evtl. meine Blindheit, oder Unwissenheit erklären?

Ich habe das Bsp von KrokoDiehl mal ein wenig abgewandelt. 
Wenn ich das wie im folgenden Code-Bsp mache, funktioniert alles einwandfrei:
Die Table wechselt "zufällig" in irgendeiner Zeile die Farbe.


```
public static void main(String[] args) {
		final JTable table = new JTable(new ColoredTableModel());
		table.setDefaultRenderer(Integer.class, new LineRenderer());

		final JFrame frame = new JFrame("Color Line Demo");
		frame.add(new JScrollPane(table));
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.pack();

		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				frame.setVisible(true);
			}
		});

		// nach 2 Sek. Farbe in Zeile 2 ändern
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					while (true) {
						ColoredTableModel model = (ColoredTableModel) table
								.getModel();
						int r = (int) (Math.random() * 255);
						int g = (int) (Math.random() * 255);
						int b = (int) (Math.random() * 255);
						int zeile = (int) (Math.random() * 5);
						Color c = new Color(r, g, b);
						Thread.sleep(250);
						model.setColorAt(zeile, c);
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}).start();
	}
```
Will ich aber bspw. aus dem lineColors-ColorArray eine Farbe auswählen, erhalte ich nach kurzer Zeit immer nur die gleiche Farbe zurück. Dafür habe ich das lineColor[] aus Zugriffsgründen mal static gemacht...aber auch mit Zugriff über eine Instanzvariable habe ich das gleiche Problem/Phänomen.
Die Zufallszahlen sehen "relativ" normal aus, aber er liefert mir irgendwann nur die gleiche Farbe zurück...hier nochmal das KSKB dazu.


```
import java.awt.Color;
import java.awt.Component;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableCellRenderer;

public final class ColorLineDemo {

	private static final class ColoredTableModel extends AbstractTableModel {

		private static Color[] lineColors;

		public ColoredTableModel() {
			super();
			lineColors = new Color[] { Color.red, Color.green, Color.blue,
					Color.lightGray, Color.cyan };
		}

		public void setColorAt(int rowIndex, Color color) {
			lineColors[rowIndex] = color;
			// wichtig für Repaint: mitteilen dass sich Zeile geändert hat
			this.fireTableRowsUpdated(rowIndex, rowIndex);
		}

		public Color getColorAt(int rowIndex) {
			return lineColors[rowIndex];
		}

		@Override
		public Class<?> getColumnClass(int columnIndex) {
			return Integer.class;
		}

		@Override
		public int getRowCount() {
			return 5;
		}

		@Override
		public int getColumnCount() {
			return 5;
		}

		@Override
		public Object getValueAt(int rowIndex, int columnIndex) {
			return rowIndex * columnIndex;
		}

	} // class ColoredTableModel

	private static final class LineRenderer extends DefaultTableCellRenderer {
		@Override
		public Component getTableCellRendererComponent(JTable table,
				Object value, boolean isSelected, boolean hasFocus, int row,
				int column) {
			Component c = super.getTableCellRendererComponent(table, value,
					isSelected, hasFocus, row, column);

			if (!isSelected) {
				ColoredTableModel model = (ColoredTableModel) table.getModel();
				Color color = model.getColorAt(table
						.convertRowIndexToModel(row)); // wichtig so
				c.setBackground(color);
			}

			return c;
		}
	} // class LineRenderer

	public static void main(String[] args) {
		final JTable table = new JTable(new ColoredTableModel());
		table.setDefaultRenderer(Integer.class, new LineRenderer());

		final JFrame frame = new JFrame("Color Line Demo");
		frame.add(new JScrollPane(table));
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.pack();

		SwingUtilities.invokeLater(new Runnable() {
			@Override
			public void run() {
				frame.setVisible(true);
			}
		});

		// nach 2 Sek. Farbe in Zeile 2 ändern
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					while (true) {
						ColoredTableModel model = (ColoredTableModel) table
								.getModel();
						int zeile = (int) (Math.random() * 5);
						int farbe = (int) (Math.random() * 5);
						Color c = ColoredTableModel.lineColors[farbe];
						Thread.sleep(250);
						model.setColorAt(zeile, c);
						System.out.println(zeile + "\t" + farbe + "\t" + c);
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}).start();
	}
}
```

[edit]wobei evtl. erwähnt werden sollte, dass die Farbe die irgendwann immder die gleiche ist, variert.[/edit]


----------



## Michael... (9. Feb 2012)

jgh hat gesagt.:


> Will ich aber bspw. aus dem lineColors-ColorArray eine Farbe auswählen, erhalte ich nach kurzer Zeit immer nur die gleiche Farbe zurück. Dafür habe ich das lineColor[] aus Zugriffsgründen mal static gemacht...aber auch mit Zugriff über eine Instanzvariable habe ich das gleiche Problem/Phänomen.
> Die Zufallszahlen sehen "relativ" normal aus, aber er liefert mir irgendwann nur die gleiche Farbe zurück...hier nochmal das KSKB dazu.


Du nutzt scheinbar das Array lineColors als mögliche Farbauswahl aber auch gleichzeitig um die Zeilenfarbe zu definieren. In der Schleife änderst dann den Inhalt des Arrays. Sobald eine beliebig Farbe mehrfach im Array vorkommt steigt die Wahrscheinlichkeit, dass diese Farbe wieder "gezogen" wird und irgendwann referenzieren alle Einträge des Arrays auf die selbe Farbe.
==> man bräuchte ein zweites Array, welches die möglichen Farben anbietet


----------



## jgh (9. Feb 2012)

Michael... hat gesagt.:


> Du nutzt scheinbar das Array lineColors als mögliche Farbauswahl aber auch gleichzeitig um die Zeilenfarbe zu definieren.


jo, indem ich mit einem zufälligen Index mir eine Farbe geben lasse.



Michael... hat gesagt.:


> In der Schleife änderst dann den Inhalt des Arrays.


mmmh, das wäre ungewollt...aber wo sollte ich das machen?
Der einzige Zugriff auf das lineColor-Array findet imho hier statt:
[java=100]  Color c = ColoredTableModel.lineColors[farbe];[/code]



Michael... hat gesagt.:


> Sobald eine beliebig Farbe mehrfach im Array vorkommt steigt die Wahrscheinlichkeit, dass diese Farbe wieder "gezogen" wird und irgendwann referenzieren alle Einträge des Arrays auf die selbe Farbe.



sofern deine 2.Vermutung zutrifft, wäre das für mich nachvollziehbar, aber da ich eigentlich nur aus dem Array eine zufällige Farbe nehme, sollte das ja nicht der Fall sein.


----------



## Michael... (9. Feb 2012)

jgh hat gesagt.:


> mmmh, das wäre ungewollt...aber wo sollte ich das machen?
> Der einzige Zugriff auf das lineColor-Array findet imho hier statt:
> [java=100]  Color c = ColoredTableModel.lineColors[farbe];[/code]


siehe:


jgh hat gesagt.:


> [JAVA=102]model.setColorAt(zeile, c);[/code]


und was der Methodenaufruf bewirkt.


jgh hat gesagt.:


> sofern deine 2.Vermutung zutrifft


War keine Vermutung, war eine Feststellung ;-)


----------



## jgh (9. Feb 2012)

shame on me...thx


----------



## may24 (10. Feb 2012)

Also das funzt vorne und hinten nicht bei mir...
Das mit dem Überschreine ging zwar, aber so recht gefällt mir das gar nicht. Zumal das neue ColoredTableModel (das ja vom AbstractTableModel erbt) kein addRow kennt und sowas "zu Fuß" implementiert werden müßte.

Ich hab's jetzt ganz anders gelöst:


```
public class MyTableCellRender extends DefaultTableCellRenderer   
{
	private Vector<Integer> lineColors = new Vector<Integer>();
	private int color;
	
    public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
    {
    	Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
    	
    	color = lineColors.get(row);
    	
    	if ( color == 1 )
    	{
    		c.setBackground(new Color(240,240,240));
    	}
    	
    	if ( color == 2 )
    	{
    		c.setBackground(new Color(220,220,220));
    	}

    	return c;
    }
    
    public void updateLineColor(int color)
    {
    	lineColors.add(color);
    }

}
```

Mein ControlModel ändert nun nach jedem übergebenen Block die "color" Information.


----------



## Michael... (10. Feb 2012)

Alternative zur Verwendung des Renderers, wäre das überschreiben der prepareRenderer der JTable.

Hinweis zu Deinem Renderer:
- erzeuge die Color Objekte nicht immer neu, sondern einmalig im Konstruktor
- man könnte die zwei Color Objekte in einem Array verwalten, dann könnte man sich z.B. die Fall unterscheidung sparen. Auf jeden Fall solte man aus dem zweiten ein 
	
	
	
	





```
else if
```
 machen

Mein Vorschlag:

```
class MyTableCellRender extends DefaultTableCellRenderer {
	private Vector<Integer> lineColors = new Vector<Integer>();
	private Color[] bgcolor = new Color[] { new Color(240, 240, 240), new Color(220, 220, 220) };

	public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
		super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
		if (row<lineColors.size())
			setBackground(bgcolor[lineColors.get(row)]);
		return this;
	}

	public void updateLineColor(int color) {
		lineColors.add(color);
	}
}
```


----------

