Reputation: 7789
I am trying to be able to color separate cells in a JTable, but I got only so far to apply a TableCellRenderer on a whole column, which then obviously malfunctions. I have a custom JTable:
public class JColorTable extends JTable{
(...)
public void setCellColor(int col, int row, Color newColor) {
getColumnModel().getColumn(col).setCellRenderer(new ColorField(col, row, newColor, background));
repaint();
}
}
ColorField looks like this:
class ColorField extends DefaultTableCellRenderer {
(...))
@Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
JLabel l = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (row == newRow && column == newCol) {
l.setBackground(Color.red);
} else {
l.setBackground(defaultColor);
}
return l;
}
}
This works like a charm when I have a single colored cell in a column, but when I try to color another cell in that column, the previous gets deleted (due to the condition in ColorField not applying for the previous column).
Is there a way to only apply ColorField to a single cell, rather than the whole column? If so, how? I did not find anything suitable, I'm afraid.
Upvotes: 3
Views: 5918
Reputation: 347194
Personally, I would avoid any solution that required you to extend a JTable
. The main reason is it makes your code less portable and tightly couples your renderer to a particular implementation of JTable
. It might suit your needs, but, personally, I'm always trying to see a bigger picture/potential/reuse.
Remember that a cell renderer is bound to a table column. It's not meant to be able to provide custom rendering on a cell by cell basis, per se. What you can do though, is provided rendering logic for each cell based a on series of conditions within the renderer.
I would probably put together some kind condition/format engine, which you could use to pass the cell value and coordinates to, which would then make decisions based on the needs of the particular implementation of the engine.
class ColorField 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);
// CellFormat is an object you create to hold the various aspects
// of the cell format
// You could also pass in the selected and focused flags if you
// needed them...
// getFormatEngine is a method you would write to provide
// to gain access to the format engine
CellFormat format = getFormatEngine().getFormatFor(value, row, column);
setBackground(format.getBackground());
setForeground(format.getForeground());
setFont(format.getFont());
// Any other properties you may wish to effect...
return this;
}
}
The FormatEngine
would be the bases for making decisions about how best to format the given cell. It should be capable of providing a "default" format as well
This means you could devise a "default" renderer for particular cell types, but have the ability to supply custom rendering based on your needs without the need for multiple if
statements or extending the base render
This is also the bases for a conditional formatting engine, where you could apply a series of conditions against a given value and return an appropriate format based on the results
Upvotes: 3
Reputation: 812
I believe the correct way to implement this is via ColorHighlighter. This is said in the documentation. This is a related issue in SO.
Note: if the table allows sorting it is essential to implement a corresponding ComponentAdapter, with its dapter.convertRowIndexToModel
Upvotes: 0
Reputation: 12985
JTable
has a method getCellRenderer()
that you can override. It is called when a cell needs rendering and returns one based on the row and column.
Your JTable
would need to keep some record of which renderer to use for each cell (by row and column). A 2D array would do it or a Map
with the key being an X,Y value.
Add a method to set the renderer on a particular cell (by row and column) and there you go.
class MyTable extends JTable {
// all the other JTable stuff goes here too ...
public TableCellRenderer getCellRenderer(int row, int column) {
TableCellRenderer myRenderer = getCustomRenderer(row, column);
if (myRenderer != null) {
return myRenderer;
}
// else...
return super.getCellRenderer(row, column);
}
private Map<Integer, Map<Integer, TableCellRenderer>> rendererMap = new ...;
public void setCustomRenderer(int row, int column, TableCellRenderer renderer) {
Map<Integer, TableCellRenderer> m1 = rendererMap.get(row);
if (m1 == null) {
m1 = new ...;
rendererMap.put(row, m1);
}
m1.put(column, renderer);
}
public TableCellRenderer getCustomRenderer(int row, int column) {
Map<Integer, TableCellRenderer> m1 = rendererMap.get(row);
if (m1 == null) {
return null;
}
return m1.get(column);
}
}
The default version of getTableCellRenderer
uses the renderer set on the column if there is one, if not it uses the renderer based on the class of the cell contents. Default cell contents is Object
in many cases. It depends on the TableModel
used.
Upvotes: 3
Reputation: 882
You need some color information for each cell in a column. This information could reside in the values:
class CellValue {
Color getBackgroundColor() { ... }
String getCellContent() { ... }
}
Your getTableCellRendererComponent()
method could then look like this:
CellValue cellValue = (CellValue) value;
Color bgColor = cellValue.getBackgroundColor();
String text = cellValue.getCellContent();
JLabel l = (JLabel) super.getTableCellRendererComponent(table, text, isSelected, hasFocus, row, column);
l.setBackground(bgColor);
...
Of course you have to adapt your TableModel to store CellValue instances.
Upvotes: 0