# TableCellRenderer UND prepareRenderer



## UnkiDunki (15. Apr 2010)

Hi,

ich habe eine Frage zum Gebrauch des TableCellRenderers:

Ist es möglich gleichzeitig mit einer Table-Klasse zu arbeiten, worin ich die prepareRenderer-Methode überschrieben habe und an anderer Stelle mit setDefaultRenderer einen TableCellRenderer setzen möchte und beides sozusagen in Kombination funktioniert... Wahrscheinlich stehe ich da auf dem Schlauch, wie dieses genau vorzunehmen ist...


----------



## André Uhres (15. Apr 2010)

Ein Renderer ist immer notwendig. Wenn man keinen eigenen setzt, wird ein Standardrenderer eingesetzt.
Unabhängig davon kann die Methode prepareRenderer immer zusätzlich benutzt werden. Wie der Name andeutet, wird er dem Renderer vorgelagert um allgemeinere Darstellungen umzusetzen, wie z.B. Zeilen einzufärben oder einen speziellen Rand um die ausgewählte Zeile  zu malen.


----------



## UnkiDunki (15. Apr 2010)

Hi,

danke für die Erklärung. Das Problem ist und auch der Grund, warum ich hier poste: Bei mir spielt beides nicht zusammen. So, wie ich das hier sehe, wird nur der Code im überschriebenen prepareRenderer() umgesetzt, aber irgendwie nicht das, was ich hinterher in einem eigenen Renderer, den ich mit setDefaultRenderer() setze, realisiert haben möchte.

Kann das sein oder liegt der Fehler dann woanders, denn nach deiner Erklärung müsste das ganze ja dann zusammenspielen können...

Nachfrage: Als ersten Parameter möchte setDefaultRenderer ja eine Klasse haben, z.B. Double.class. Bedeutet das dann, dass er nur die Zellen von diesem Typ entsprechend formatiert? Und wenn man dieses allgemein halten will, wie müsste was müsste man dann übergeben oder macht das ganze dann keinen Sinn?


----------



## André Uhres (16. Apr 2010)

Ein Renderer gilt für eine Spalte oder für einen Datentyp (gemäß "getColumnClass" im "TableModel"). Die Methode "prepareRenderer" wird für zeilen- oder tabellenabhängige Erfordernisse genutzt. Sie kann keine Renderer ausschalten sondern nur "vorbereiten". 
Wenn du noch Hilfe brauchst, mach bitte ein kurzes, selbständiges und kompilierbares Beispiel.


----------



## UnkiDunki (22. Apr 2010)

Hi André,

Deine Erklärungen und dein Hilfsangebot weiss ich sehr zu schätzen 
Ich habe jetzt mal den Renderer aus der FAQ http://www.java-forum.org/java-faq-beitraege/7032-jtable-teil-4-darstellung-daten.html (Punkt 3) übernommen und konnte das Problem jetzt eingrenzen. Die Zellen werden alle ordnungsgemäß gerendert, was aber NICHT funktioniert, ist der Farbwechsel beim Selektieren einer Zeile/Zelle...

Das Problem ist das Zusammenspiel zwischen diesem beschrieben Farbwechsel

```
// die normalen Farben
        setForeground( Color.BLACK );
        if( hasFocus )
            setBackground( colorFocus );
        else if( isSelected )
            setBackground( colorSelected );
        else
            setBackground( colorNormal );
```
 und einer voher getätigten Einfärben der Zeilen/Zellen abhängig von deren Inhalten. Vielleicht erinnerst du dich, denn auch dabei hast du mir geholfen:


```
public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
	        Component c = super.prepareRenderer(renderer, row, column);
	        int rowM = convertRowIndexToModel(row);
	        int red = Integer.valueOf(this.getModel().getValueAt(rowM, RED).toString());
	        int green = Integer.valueOf(this.getModel().getValueAt(rowM, GREEN).toString());
	        int blue = Integer.valueOf(this.getModel().getValueAt(rowM, BLUE).toString());
	        c.setBackground(new Color(red, green, blue));
	        return c;
	    }
```

Ich könnte mir jetzt erklären, warum eine Zelle/Zeile nicht ihre ursprüngliche Farbe zurückerhält, wenn sie den Focus verliert bzw. nicht mehr ausgewählt ist, aber warum sie bei ihrer Selektierung anfänglich nicht wenigstens mit den Farben colorFocus oder colorSelected eingefärbt wird nicht...

Problem 1: Warum ändert sich die Farbe bei Selektion nicht?
Problem 2: colorNormal ist ja bei jeder Zelle/Zeile verschieden, wie erreicht man bei z.B. Verlust des Focuses eine Zurücksetzung auf diese?
Wäre das schon mit einem anfänglichen 
	
	
	
	





```
colorNormal = getBackground();
```
 getan? Ausprobieren kann ich es ja leider noch nicht, da die grundsätzliche Selektionseinfärbung nicht funktioniert...

Vielen Dank im Voraus für Anregungen


----------



## Michael... (22. Apr 2010)

Hast mal ein KSKB zu dem was Du vorhast?
Da Du nach in der überschriebenen prepareRenderer() nach dem Aufruf von super.prepareRenderer() in Zeile 7 die Hintergrundfarbe setzt, müsste das Setzen des Hintergrunds im DefaultCellRenderer "ignoriert" werden.


----------



## UnkiDunki (22. Apr 2010)

Hi,

gerne mache ich dir ein KSKB. Hoffe, es läuft  LG

P.S.: Die Zeile wird gemäß den drei Integerwerten in Columns 4,5 und 6 grün eingefärbt und auch z.B. der Booleanwerte gemäß dem Renderer in einen String verändert. Eine merkliche (Farbveränderung) Zeilenauswahl tut sich aber leider nicht...


```
package main;
import java.awt.Color;
import java.awt.Component;
import java.util.Date;

import javax.swing.Icon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;

public class KSKB{

   public static void main( String[] args ){
      // Unser TableModel (siehe unten)
     KSKBTableModel model = new KSKBTableModel();
      Object[] objects = new Object[]{"Text",Float.valueOf( 5.123f ),true,new Date( System.currentTimeMillis() ),110,210,98};
      model.addRow(objects);

      // Das JTable initialisieren
      MyColoredJTable table = new MyColoredJTable();
      table.setDefaultRenderer(Object.class, new CellRenderer());
      table.setModel(model);

      JFrame frame = new JFrame( "Demo" );
      frame.getContentPane().add( new JScrollPane( table ) );

      frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
      frame.pack();
      frame.setVisible( true );
   }
}

class KSKBTableModel extends DefaultTableModel {

	private static final long serialVersionUID = 1L;

	public KSKBTableModel(){
		super();
		setColumnIdentifiers(new String[]{"String","Float","Boolean","Date","ColorR", "ColorG", "ColorB"});
	}

	@SuppressWarnings("unchecked")
	public Class getColumnClass(int columnIndex) {

		switch (columnIndex) {
		case 0:
			return String.class;
		case 1:
			return Float.class;
		case 2:
			return String.class;
		case 3:
			return Date.class;
		case 4:
			return Integer.class;
		case 5:
			return Integer.class;
		case 6:
			return Integer.class;
		default:
			return null;
		}
	}
}

class CellRenderer extends JLabel implements TableCellRenderer {

	/**
	 *
	 */
	private static final long serialVersionUID = 1L;

	private Color colorSelected = new Color( 200, 255, 200 );
    private Color colorFocus = new Color( 255, 200, 200 );
    private Color colorNormal = new Color( 200, 200, 255 );

	public CellRenderer() {
		setOpaque(true);
	}

	public Component getTableCellRendererComponent(JTable table, Object value,
			boolean isSelected, boolean hasFocus, int row, int column) {
		// TODO Auto-generated method stub

		colorNormal = getBackground();
		 // die normalen Farben
        setForeground( Color.BLACK );
        if( hasFocus )
            setBackground( colorFocus );
        else if( isSelected )
            setBackground( colorSelected );
        else
            setBackground( colorNormal );

        setText( null );
        setIcon( null );

        if( value instanceof Date )
            setText( ((Date)value).toGMTString() );
        else if( value instanceof Icon )
            setIcon( (Icon)value );
        else if( value instanceof Color ){
            Color color = (Color)value;
            setForeground( color );
            setText( color.getRed() + ", " + color.getGreen() + ", " + color.getBlue() );
        }
        else if( value instanceof Boolean ){
            if( ((Boolean)value).booleanValue() )
                setText( "yes" );
            else
                setText( "no" );
        }
        else if(value != null)
            setText( value.toString() );

        return this;
	}
}

class MyColoredJTable extends JTable {

	/**
	 *
	 */
	private static final long serialVersionUID = 1L;
	private int RED = 4;	//column index of the red color part
    private int GREEN = 5;	//column index of the green color part
    private int BLUE = 6;	//column index of the blue color part

	public MyColoredJTable() {
		// TODO Auto-generated constructor stub
	}

	public MyColoredJTable(DefaultTableModel tableModel) {
		super(tableModel);
		// TODO Auto-generated constructor stub
	}

	@Override
	    public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
	        Component c = super.prepareRenderer(renderer, row, column);
	        int rowM = convertRowIndexToModel(row);
	        int red = Integer.valueOf(this.getModel().getValueAt(rowM, RED).toString());
	        int green = Integer.valueOf(this.getModel().getValueAt(rowM, GREEN).toString());
	        int blue = Integer.valueOf(this.getModel().getValueAt(rowM, BLUE).toString());
	        c.setBackground(new Color(red, green, blue));
	        return c;
	    }
}
```


----------



## Michael... (22. Apr 2010)

Naja, durch die überschriebene prepareRenderer() machst Du ja jede im CellRenderer gesetzte Hintergrundfarbe zu nichte.

Worum geht's Dir genau? Willst Du nur, dass man die Selektion in der Zeile erkennt (soll die Standardselektion verwendet werden) und die anderen Zeilen entsprechend der drei in den letzten drei Spalten eingefärbt werden?


----------



## UnkiDunki (22. Apr 2010)

Michael... hat gesagt.:


> Worum geht's Dir genau? Willst Du nur, dass man die Selektion in der Zeile erkennt (soll die Standardselektion verwendet werden) und die anderen Zeilen entsprechend der drei in den letzten drei Spalten eingefärbt werden?



Genau. Eine Selektion einer Zeile soll erkennbar sein und grundsätzlich alle Zeilen standardmäßig mit ihren RGB-Werten aus den drei letzten Spalten eingefärbt sein und diese Farbe bei Nicht-Selektion wieder annehmen.


----------



## Michael... (22. Apr 2010)

Probier mal ob folgendes Dein Problem löst. Funktioniert allerdings noch nicht bei Selektion mehrerer Zeilen und ich habe in der prepareRenderer die Zeilenkonvertierung (Zeile 51) 
	
	
	
	





```
int rowM = convertRowIndexToModel(row);
```
 raus gemacht, da diese Methode erst ab 1.6 zur Verfügung steht und ich hier max 1.5 habe.


```
import java.awt.Color;
import java.awt.Component;
import java.text.DateFormat;
import java.util.Date;

import javax.swing.Icon;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;

public class KSKB {

	private static int RED = 4; // column index of the red color part
	private static int GREEN = 5; // column index of the green color part
	private static int BLUE = 6; // column index of the blue color part
	private static JTable table;

	public static void main(String[] args) {
		DefaultTableModel model = new DefaultTableModel(new Object[][] {
				{ "Text", 5.123f, true, new Date(System.currentTimeMillis()), 110, 210, 98 },
				{ "Text", 4.123f, true, new Date(System.currentTimeMillis()), 110, 200, 200 } }, 
				new String[] { "String", "Float", "Boolean", "Date", "ColorR", "ColorG", "ColorB" }) {
			public Class getColumnClass(int columnIndex) {
				switch (columnIndex) {
				case 0:
					return String.class;
				case 1:
					return Float.class;
				case 2:
					return String.class;
				case 3:
					return Date.class;
				case 4:
				case 5:
				case 6:
					return Integer.class;
				default:
					return null;
				}
			}
		};
		table = new JTable(model) {
			public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
				Component c = super.prepareRenderer(renderer, row, column);
				if (table.getSelectedRow()==row)
					return c;
				
				int rowM = row;
				int red = Integer.valueOf(this.getModel().getValueAt(rowM, RED).toString());
				int green = Integer.valueOf(this.getModel().getValueAt(rowM, GREEN).toString());
				int blue = Integer.valueOf(this.getModel().getValueAt(rowM,	BLUE).toString());
				c.setBackground(new Color(red, green, blue));
				return c;
			} 
		};
		table.getSelectedRows();
		table.setDefaultRenderer(Object.class, new CellRenderer());

		JFrame frame = new JFrame("Demo");
		frame.getContentPane().add(new JScrollPane(table));
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.pack();
		frame.setVisible(true);
	}
}

class CellRenderer extends DefaultTableCellRenderer {

	public Component getTableCellRendererComponent(JTable table, Object value,
			boolean isSelected, boolean hasFocus, int row, int column) {
		super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
		
		if (value instanceof Date)
			setText(DateFormat.getDateInstance().format((Date) value));
		else if (value instanceof Icon)
			setIcon((Icon) value);
		else if (value instanceof Boolean) {
			if (((Boolean) value).booleanValue())
				setText("yes");
			else
				setText("no");
		}
		return this;
	}
}
```


----------



## UnkiDunki (22. Apr 2010)

Hi Michael...

danke erstmal für das schöne Formatieren meines KSKBs. Gib zu, dass das sowas wie ein Putzfimmel ist 
Das Geheimnis ist also folgende Zeile 
	
	
	
	





```
if (getSelectedRow()==row) return c;
```
 Da wäre ich nicht drauf gekommen...
Und ja, schade, dass es bei Multiselektion nicht funktioniert, aber schon mal nicht schlecht! Vielen Dank


----------



## Michael... (23. Apr 2010)

Hatte gestern ein bisschen Frust und da kam mir halt Dein Code in die Finger ;-)

Man kann das ja durchaus erweitern, damit das auch mit der Multiselektion funktionert. Es gibt ja auch noch sowas wie getSelectedRow*s*() usw. Eventuell gibt's ja noch eine bessere Möglichkeit.


----------



## UnkiDunki (23. Apr 2010)

Hi,

ok, stimmt, bei getSelectedRows() werde ich noch mal ansetzen. Das ist ja auch noch da 

Noch mal ne allgemeinere Frage:
Ich habe jetzt einen TableCellRenderer im Einsatz, der sich um alle Objekte kümmern soll. Die Frage ist jetzt woher er weiss, von welcher Instanz die "value" ist? Das müsste doch vom TableModel kommen, oder nicht?
Ich frage deswegen, weil er anscheinend bei mir nur Booleans erkennt und rendert, aber bei z.B. BigDecimal, etc. irgendwie garnichts macht.
Wenn ich also z.B. BigDecimals rendern möchte, muss ich einen DefaultTableCellRenderer benutzen und dort die Methode setValue überschreiben. Wenn ich deren Inhalt aber im TableCellRender in die if-Abfrage value instanceof BigDecimal packe, tut sich garnichts...
Fakt ist, dass ich eigentlich nur für Booleans wirklich Anweisungen zum Rendern geben kann... Mhmm...
Wie kommt das?

Hat das vielleicht damit zu tun, wann ich den DefaultRenderer setze oder ist das egal? Also vor oder nachdem ich das TableModel (neu)setze.


----------

