# DefaultTableCellRenderer (JXTreeTable)



## lottoFee2010 (4. Okt 2012)

Hallo zusammen,

ich habe ein kleines Problem mit dem DefaultTableCellRenderer und zwar
möchte ich die hintergrundfarbe einer Zeile rot makieren wenn in der zeile selbst einzelne Zellen bestimmte Kriterien erfüllen.

CellRenderer wende ich auf JXTreeTable an.
Meine bisherigen Versuche klappten nicht so richtig, die Zeile wird nur dann Rot markiert wenn sie auch ausgewählt ist.

Hat jemand eine Idee?

Hier ein Ausschintt aus meinem Renderer:

```
public class ErgebnisTableCellRenderer extends DefaultTableCellRenderer {

	@Override
	public Component getTableCellRendererComponent(JTable table, Object value,
			boolean isSelected, boolean hasFocus, int row, int column) {
		super.getTableCellRendererComponent(table, value, isSelected, hasFocus,
				row, column);

		// setBackground(Color.BLUE); //test

		Object v1 = table.getValueAt(row, 1);
		Object v2 = table.getValueAt(row, 2);

		if (v1 != null && v2 != null) {
			String str1 = ((String) v1).toLowerCase();
			String str2 = ((String) v2).toLowerCase();
			if (str1.equals("nein") && str2.equals("x")) {
				setBackground(Color.RED);
			}
		}
		return this;
	}
}
```

vielen Dank im vorraus


----------



## Marco13 (4. Okt 2012)

Funktioniert das auch mit einer normalen JTable? Dann wäre ein KSKB nicht schlecht. 

Allgemein ist es aber so, dass man NICHT sowas machen darf wie

```
if (someSpecialPropertyForCell())
{
    setBackground(Color.RED);
}
```
sondern man unbedingt auch den else-Teil angeben muss

```
if (someSpecialPropertyForCell())
{
    setBackground(Color.RED);
}
else
{
    setBackground(defaultBackgroundColor);
```
weil sich das "RED" sonst auf alle nachfolgend grenderten Zellen bezieht.


----------



## lottoFee2010 (5. Okt 2012)

Danke für die Antwort



Marco13 hat gesagt.:


> weil sich das "RED" sonst auf alle nachfolgend grenderten Zellen bezieht.



Genua das wollte ich ja erreichen.

Nach dem ich stunden lang in SwingX Froum auf der suche war, fand ich eine Lösung:

Der JXTreeTable arbeitet nicht mit dauerhaft mit dem CellRenderern sondern mit: 

org.jdesktop.swingx.renderer.DefaultTableRenderer
org.jdesktop.swingx.renderer.DefaultTreeRenderer

Es ist leider nicht so einfach auf ein KSKB zu reduzieren.


Es wird SwingX benötigt!!:exclaim:
SwingX &mdash; Java.net

Ich habe es mal so klein wie möglich gemacht:
MainKlasse:

```
package forum;

import java.util.Arrays;

import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.ListSelectionModel;

import org.jdesktop.swingx.JXTreeTable;
import org.jdesktop.swingx.treetable.DefaultTreeTableModel;

public class FragenTreeTable {

	private final String[] headings = { "Frage & Antwort", "Wahr", "Antwort" };
	private Node root;
	private DefaultTreeTableModel model;
	private JXTreeTable table;

	public FragenTreeTable() {

	}

	private JXTreeTable getTreeTable() {
		root = new RootNode("Umfrage");

		ChildNode child = new ChildNode(
				new String[] { "Wer war der erste Mann auf dem Mond?" });
		root.add(child);
		child.add(new ChildNode(new String[] { "Tim", "Nein" }));
		child.add(new ChildNode(new String[] { "Kai", "Nein", "x" }));
		child.add(new ChildNode(new String[] { "Neil", "Ja" }));

		child = new ChildNode(new String[] { "Wann war ..." });
		root.add(child);
		child.add(new ChildNode(new String[] { "1990", "Nein" }));
		child.add(new ChildNode(new String[] { "1991", "Ja", "x" }));
		child.add(new ChildNode(new String[] { "2012", "Ja" }));
		child = new ChildNode(new String[] { "Wann war ..." });
		root.add(child);
		child.add(new ChildNode(new String[] { "1990", "Nein", "x" }));
		child.add(new ChildNode(new String[] { "1991", "Ja", "x" }));
		child.add(new ChildNode(new String[] { "2012", "Ja" }));
		child = new ChildNode(new String[] { "Wann war ..." });
		root.add(child);
		child.add(new ChildNode(new String[] { "1990", "Nein" }));
		child.add(new ChildNode(new String[] { "1991", "Ja", }));
		child.add(new ChildNode(new String[] { "2012", "Ja", "x" }));

		model = new DefaultTreeTableModel(root, Arrays.asList(headings)) {
			@Override
			public Class<Object> getColumnClass(int column) {
				return Object.class;
			}
		};
		table = new JXTreeTable(model);
		table.setShowGrid(true, true);
		table.setRootVisible(false);
		table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
		table.setColumnControlVisible(true);
		table.setHorizontalScrollEnabled(true);
		table.setFillsViewportHeight(false);
		table.setDefaultRenderer(Object.class, new MyDefaultTableRenderer());
		table.setTreeCellRenderer(new MyDefaultTreeRenderer());
		table.setRowHeight(25);

		table.expandAll();
		table.packAll();

		return table;
	}

	public JScrollPane getFragenTreeTable() {
		return new JScrollPane(getTreeTable());
	}

	private static void createAndShowGUI() {
		JFrame frame = new JFrame("JXTreeTable");
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

		frame.setContentPane(new FragenTreeTable().getFragenTreeTable());

		frame.pack();
		frame.setVisible(true);
	}

	public static void main(String[] args) {
		javax.swing.SwingUtilities.invokeLater(new Runnable() {
			public void run() {
				createAndShowGUI();
			}
		});
	}

}
```

Node Klassen:

```
package forum;

import org.jdesktop.swingx.treetable.AbstractMutableTreeTableNode;

public abstract class Node extends AbstractMutableTreeTableNode {

	public Node(Object[] data) {
		super(data);
		if (data == null) {
			throw new IllegalArgumentException("Node data cannot be null");
		}
	}

	/*
	* Inherited
	*/
	@Override
	public int getColumnCount() {
		return getData().length;
	}

	/*
	* Inherited
	*/
	@Override
	public Object getValueAt(int column) {
		return getData()[column];
	}

	public Object[] getData() {
		return (Object[]) getUserObject();
	}
}
```


```
package forum;

public class RootNode extends Node {

	public RootNode(String key) {
		super(new Object[] { key });
	}

}
```


```
package forum;

public class ChildNode extends Node {

	public ChildNode(Object[] data) {
		super(data);
	}

}
```

Render Klassen:

```
package forum;


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

import javax.swing.JTable;
import javax.swing.table.TableCellRenderer;
import javax.swing.tree.TreePath;

import org.jdesktop.swingx.JXTreeTable;
import org.jdesktop.swingx.renderer.AbstractRenderer;
import org.jdesktop.swingx.renderer.ComponentProvider;
import org.jdesktop.swingx.renderer.IconValue;
import org.jdesktop.swingx.renderer.LabelProvider;
import org.jdesktop.swingx.renderer.MappedValue;
import org.jdesktop.swingx.renderer.StringValue;
import org.jdesktop.swingx.renderer.TableCellContext;

@SuppressWarnings("serial")
public class MyDefaultTableRenderer extends AbstractRenderer implements
		TableCellRenderer {

	private TableCellContext cellContext;

	/**
	 * Instantiates a default table renderer with the default component
	 * provider.
	 * 
	 * @see #DefaultTableRenderer(ComponentProvider)
	 */
	public MyDefaultTableRenderer() {
		this((ComponentProvider<?>) null);
	}

	/**
	 * Instantiates a default table renderer with the given component provider.
	 * If the controller is null, creates and uses a default. The default
	 * provider is of type <code>LabelProvider</code>.
	 * 
	 * @param componentProvider
	 *            the provider of the configured component to use for cell
	 *            rendering
	 */
	public MyDefaultTableRenderer(ComponentProvider<?> componentProvider) {
		super(componentProvider);
		this.cellContext = new TableCellContext();
	}

	/**
	 * Instantiates a default table renderer with a default component provider
	 * using the given converter.
	 * 
	 * @param converter
	 *            the converter to use for mapping the content value to a String
	 *            representation.
	 * 
	 * @see #DefaultTableRenderer(ComponentProvider)
	 */
	public MyDefaultTableRenderer(StringValue converter) {
		this(new LabelProvider(converter));
	}

	/**
	 * Instantiates a default table renderer with a default component provider
	 * using the given converter and horizontal alignment.
	 * 
	 * @param converter
	 *            the converter to use for mapping the content value to a String
	 *            representation.
	 * 
	 * @see #DefaultTableRenderer(ComponentProvider)
	 */
	public MyDefaultTableRenderer(StringValue converter, int alignment) {
		this(new LabelProvider(converter, alignment));
	}

	/**
	 * Intantiates a default table renderer with default component provider
	 * using both converters.
	 * 
	 * @param stringValue
	 *            the converter to use for the string representation
	 * @param iconValue
	 *            the converter to use for the icon representation
	 */
	public MyDefaultTableRenderer(StringValue stringValue, IconValue iconValue) {
		this(new MappedValue(stringValue, iconValue));
	}

	/**
	 * Intantiates a default table renderer with default component provider
	 * using both converters and the given alignment.
	 * 
	 * @param stringValue
	 *            the converter to use for the string representation
	 * @param iconValue
	 *            the converter to use for the icon representation
	 * @param alignment
	 *            the rendering component's horizontal alignment
	 */
	public MyDefaultTableRenderer(StringValue stringValue, IconValue iconValue,
			int alignment) {
		this(new MappedValue(stringValue, iconValue), alignment);
	}

	// -------------- implements javax.swing.table.TableCellRenderer
	/**
	 * 
	 * Returns a configured component, appropriate to render the given list
	 * cell.
	 * <p>
	 * 
	 * Note: The component's name is set to "Table.cellRenderer" for the sake of
	 * Synth-based LAFs.
	 * 
	 * @param table
	 *            the <code>JTable</code>
	 * @param value
	 *            the value to assign to the cell at <code>[row, column]</code>
	 * @param isSelected
	 *            true if cell is selected
	 * @param hasFocus
	 *            true if cell has focus
	 * @param row
	 *            the row of the cell to render
	 * @param column
	 *            the column of the cell to render
	 * @return the default table cell renderer
	 */
	@Override
	public Component getTableCellRendererComponent(JTable table, Object value,
			boolean isSelected, boolean hasFocus, int row, int column) {
		cellContext.installContext(table, value, row, column, isSelected,
				hasFocus, true, true);
		Component comp = componentController.getRendererComponent(cellContext);
		// fix issue #1040-swingx: memory leak if value not released
		cellContext.replaceValue(null);

		if (table instanceof JXTreeTable) {
			JXTreeTable xtable = (JXTreeTable) table;
			// xtable.getModel().getValueAt(row, 1);
			TreePath path = xtable.getPathForRow(row);
			Object node = path.getLastPathComponent();
			if (node instanceof ChildNode) {
				ChildNode cnode = (ChildNode) node;
				if (cnode.getData().length == 3) {

					Object v1 = cnode.getData()[1];
					Object v2 = cnode.getData()[2];

					if (v1 != null && v2 != null) {
						String str1 = ((String) v1).toLowerCase();
						String str2 = ((String) v2).toLowerCase();
						if (str1.equals("nein") && str2.equals("x")) {
							comp.setBackground(Color.RED.brighter().brighter());
							comp.setForeground(Color.BLACK);
						} else if (str1.equals("ja") && str2.equals("x")) {
							comp.setBackground(Color.GREEN.darker());
							comp.setForeground(Color.BLACK);
						}
					}
				}
			}
		}

		return comp;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected ComponentProvider<?> createDefaultComponentProvider() {
		return new LabelProvider();
	}

}
```


```
package forum;

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

import javax.swing.JLabel;
import javax.swing.JTree;
import javax.swing.tree.TreeCellRenderer;

import org.jdesktop.swingx.renderer.AbstractRenderer;
import org.jdesktop.swingx.renderer.ComponentProvider;
import org.jdesktop.swingx.renderer.IconValue;
import org.jdesktop.swingx.renderer.StringValue;
import org.jdesktop.swingx.renderer.TreeCellContext;
import org.jdesktop.swingx.renderer.WrappingProvider;

public class MyDefaultTreeRenderer extends AbstractRenderer implements
		TreeCellRenderer {

	private TreeCellContext cellContext;

	/**
	 * Instantiates a default tree renderer with the default component provider.
	 * 
	 */
	public MyDefaultTreeRenderer() {
		this((ComponentProvider<?>) null);
	}

	/**
	 * Instantiates a default tree renderer with the given component provider.
	 * If the controller is null, creates and uses a default. The default
	 * controller is of type <code>WrappingProvider</code>.
	 * 
	 * @param componentProvider
	 *            the provider of the configured component to use for cell
	 *            rendering
	 */
	public MyDefaultTreeRenderer(ComponentProvider<?> componentProvider) {
		super(componentProvider);
		this.cellContext = new TreeCellContext();
	}

	/**
	 * Instantiates a default tree renderer with the default wrapping provider,
	 * using the given IconValue for customizing the icons.
	 * 
	 * @param iv
	 *            the IconValue to use for mapping a custom icon for a given
	 *            value
	 * 
	 */
	public MyDefaultTreeRenderer(IconValue iv) {
		this(new WrappingProvider(iv));
	}

	/**
	 * Instantiates a default tree renderer with a default component provider
	 * using the given converter.
	 * 
	 * @param sv
	 *            the converter to use for mapping the content value to a String
	 *            representation.
	 * 
	 */
	public MyDefaultTreeRenderer(StringValue sv) {
		this(new WrappingProvider(sv));
	}

	/**
	 * Instantiates a default tree renderer with the default wrapping provider,
	 * using the given IconValue for customizing the icons and the given
	 * StringValue for node content.
	 * 
	 * @param iv
	 *            the IconValue to use for mapping a custom icon for a given
	 *            value
	 * @param sv
	 *            the converter to use for mapping the content value to a String
	 *            representation.
	 * 
	 */
	public MyDefaultTreeRenderer(IconValue iv, StringValue sv) {
		this(new WrappingProvider(iv, sv));
	}

	/**
	 * Instantiates a default tree renderer with the default wrapping provider,
	 * using the given IconValue for customizing the icons and the given
	 * StringValue for node content.
	 * 
	 * @param iv
	 *            the IconValue to use for mapping a custom icon for a given
	 *            value
	 * @param sv
	 *            the converter to use for mapping the content value to a String
	 *            representation.
	 * @param unwrapUserObject
	 *            a flag indicating whether this provider should auto-unwrap the
	 *            userObject from the context value.
	 * 
	 */
	public MyDefaultTreeRenderer(IconValue iv, StringValue sv,
			boolean unwrapUserObject) {
		this(new WrappingProvider(iv, sv, unwrapUserObject));
	}

	// -------------- implements javax.swing.table.TableCellRenderer
	/**
	 * 
	 * Returns a configured component, appropriate to render the given tree
	 * cell.
	 * <p>
	 * 
	 * @param tree
	 *            the <code>JTree</code>
	 * @param value
	 *            the value to assign to the cell
	 * @param selected
	 *            true if cell is selected
	 * @param expanded
	 *            true if the cell is expanded
	 * @param leaf
	 *            true if the cell is a leaf
	 * @param hasFocus
	 *            true if cell has focus
	 * @param row
	 *            the row of the cell to render
	 * @return a component to render the given list cell.
	 */
	@Override
	public Component getTreeCellRendererComponent(JTree tree, Object value,
			boolean selected, boolean expanded, boolean leaf, int row,
			boolean hasFocus) {
		cellContext.installContext(tree, value, row, 0, selected, hasFocus,
				expanded, leaf);
		Component comp = componentController.getRendererComponent(cellContext);
		cellContext.replaceValue(null);
		Object[] obj = ((ChildNode) value).getData();
		JLabel label = new JLabel(obj[0].toString());

		if (selected) {
			label.setForeground(Color.WHITE);
		}

		return label;
		// return comp;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	protected ComponentProvider<?> createDefaultComponentProvider() {
		return new WrappingProvider();
	}

}
```


----------



## Marco13 (5. Okt 2012)

lottoFee2010 hat gesagt.:


> Genua das wollte ich ja erreichen.



Sowas, vielleicht, aber nicht genau das, wie es dann rauskommen würde 

Ist das jetzt "Erledigt"? (hab's nicht getestet)


----------



## lottoFee2010 (5. Okt 2012)

Marco13 hat gesagt.:


> Sowas, vielleicht, aber nicht genau das, wie es dann rauskommen würde
> 
> Ist das jetzt "Erledigt"? (hab's nicht getestet)




Ja ist erledigt.
Die Bedingung zum einfärben habe ich noch leicht abgewandelt:

```
if (v1 != null && v2 != null) {
                        String str1 = ((String) v1).toLowerCase();
                        String str2 = ((String) v2).toLowerCase();
                        if (str1.equals("nein") && str2.equals("x")) {
                            comp.setBackground(Color.RED.brighter().brighter());
                            comp.setForeground(Color.BLACK);
                        } else if (str1.equals("ja") && str2.equals("x")) {
                            comp.setBackground(Color.GREEN.darker());
                            comp.setForeground(Color.BLACK);
                        }
                    }
```


----------



## Marco13 (5. Okt 2012)

Wie gesagt, dort sollte auf jeden Fall noch z.B. sowas wie

comp.setBackground(defaultBackground);
comp.setForeground(defaultForeground);

DAVOR stehen, wobei die beiden angegebenen Farben die sind, die verwendet werden sollen, wenn KEINE der Bedinungen zutrifft. 

Die CellRenderer-Component wird als "Stempel" verwendet. Wenn man 10 Zellen rendert, wird für alle 10 Zellen ein und dieSELBE (!) Component verwendet. Wenn man nun in der 3. Zelle die Hintergrundfarbe ändert, und sie für das Rendern der 4. (und folgenden) Zellen NICHT wieder auf den default-Wert setzt, wird für alle nachfolgenden Zellen die Hintergrundfarbe aus der 3. Zelle verwendet.


----------



## lottoFee2010 (5. Okt 2012)

Ich weiß jetzt gerade nicht ganz was du mir sagen willst?

Ich mache das doch:


```
public Component getTableCellRendererComponent(JTable table, Object value,
            boolean isSelected, boolean hasFocus, int row, int column) {
        cellContext.installContext(table, value, row, column, isSelected,
                hasFocus, true, true);
        Component comp = componentController.getRendererComponent(cellContext);
        // fix issue #1040-swingx: memory leak if value not released
        cellContext.replaceValue(null);
 
        if (table instanceof JXTreeTable) {
            JXTreeTable xtable = (JXTreeTable) table;
            // xtable.getModel().getValueAt(row, 1);
            TreePath path = xtable.getPathForRow(row);
            Object node = path.getLastPathComponent();
            if (node instanceof ChildNode) {
                ChildNode cnode = (ChildNode) node;
                if (cnode.getData().length == 3) {
 
                    Object v1 = cnode.getData()[1];
                    Object v2 = cnode.getData()[2];
 
                    if (v1 != null && v2 != null) {
                        String str1 = ((String) v1).toLowerCase();
                        String str2 = ((String) v2).toLowerCase();
                        if (str1.equals("nein") && str2.equals("x")) {
                            comp.setBackground(Color.RED.brighter().brighter());
                            comp.setForeground(Color.BLACK);
                        } else if (str1.equals("ja") && str2.equals("x")) {
                            comp.setBackground(Color.GREEN.darker());
                            comp.setForeground(Color.BLACK);
                        }
                    }
                }
            }
        }
 
        return comp;
    }
```


----------



## Marco13 (5. Okt 2012)

Ich weiß gerade nicht auswendig, was der componentController macht, vielleicht erledigt der das intern schon.


----------

