Custom TableCellRenderer/TreeTableCellRenderer doesn't render Table cells - java

I made this CustomCellRenderer class intended to be used in JXTreeTable and JXTable objects since I have many of these in my project. So this class implements TreeCellRenderer and TableCellRenderer interfaces:
public class CustomCellRenderer extends JLabel
implements TreeCellRenderer, TableCellRenderer {
#Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf, int row, boolean hasFocus) {
setBackground(selected ? new Color(83,142,213) : Color.white);
setForeground(selected ? Color.white : Color.black);
//here is the icon setting code but it's irrelevant to my problem
setText(value != null ? value.toString() : "<null>");
return this;
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
setBackground(isSelected ? new Color(83,142,213) : Color.white);
setForeground(isSelected ? Color.white : Color.black);
setText(value != null ? value.toString() : "<null>");
return this;
}
}
And here is the code where I set the renderer:
jXTreeTableConsumos.setDefaultRenderer(Object.class, new CustomCellRenderer());
jXTreeTableConsumos.setTreeCellRenderer(new CustomCellRenderer());
I'm expecting background and foreground become blue and white respectively when a row is selected. However it only happens at Tree table cell (first column) while only foreground changes and background stills white in the other cells in the very same selected row:
Could anybody please tell me why cells (that are not tree cells) don't change their background color?

Thanks everybody for your comments and suggestions. I found the solution in JComponent#setBackground(Color bg) documentation:
Sets the background color of this component. The background color is
used only if the component is opaque, and only by subclasses of
JComponent or ComponentUI implementations. Direct subclasses of
JComponent must override paintComponent to honor this property.
Since my CustomCellRenderer extends from JLabel the only thing I have to do is make sure it's opaque and its background color will be painted:
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
setOpaque(true);//adding this line I solved my problem
setBackground(isSelected ? new Color(83,142,213) : Color.white);
setForeground(isSelected ? Color.white : Color.black);
setText(value != null ? value.toString() : "<null>");
return this;
}

Related

how to use custom cell rendering in jtable for the below needs? [duplicate]

I would like to make an editable table and then check the data to make sure its valid. Im not sure how to change the color of just one cell. I would like to get a cell, for example (0,0) and color the foreground to red. I have read the other posts on SO as well as Oracle about the custom ColorRenderer, but i just don't get how i would use this.
Thanks.
Say that the cell you would like to render with a different color represents a status (I'll take Rejected and Approved as examples). I'd then implement a method in my table model called getStatus(int row) which returns the status for any given row.
Then, when that is in place, I'd go about creating a cell renderer responsible for rendering the column which the cell belongs to. The cell renderer would be something in the lines of the below code.
public class StatusColumnCellRenderer extends DefaultTableCellRenderer {
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) {
//Cells are by default rendered as a JLabel.
JLabel l = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col);
//Get the status for the current row.
CustomTableModel tableModel = (CustomTableModel) table.getModel();
if (tableModel.getStatus(row) == CustomTableModel.APPROVED) {
l.setBackground(Color.GREEN);
} else {
l.setBackground(Color.RED);
}
//Return the JLabel which renders the cell.
return l;
}
Then, when the renderer is in place, simply "apply" the renderer to the table with the following piece of code:
Table.getColumnModel().getColumn(columnIndex).setCellRenderer(new StatusColumnCellRenderer());
With regard to making a cell editable, simply implement the isCellEditable(int rowIndex, int columnIndex) method in your table model. You also need to implement the method
setValueAt(Object value, int rowIndex, int columnIndex) if you would like to keep the value which the user provides (which i assume you do!).
I would like to make an editable table and then check the data to make sure its valid.
Another approach would be to edit the data before it is saved to the table model to prevent invalid data from being entered.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.text.*;
import javax.swing.event.*;
import javax.swing.border.*;
import javax.swing.table.*;
public class TableEdit extends JFrame
{
TableEdit()
{
JTable table = new JTable(5,5);
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane scrollpane = new JScrollPane(table);
getContentPane().add(scrollpane);
// Use a custom editor
TableCellEditor fce = new FiveCharacterEditor();
table.setDefaultEditor(Object.class, fce);
}
class FiveCharacterEditor extends DefaultCellEditor
{
FiveCharacterEditor()
{
super( new JTextField() );
}
public boolean stopCellEditing()
{
try
{
String editingValue = (String)getCellEditorValue();
if(editingValue.length() != 5)
{
JTextField textField = (JTextField)getComponent();
textField.setBorder(new LineBorder(Color.red));
textField.selectAll();
textField.requestFocusInWindow();
JOptionPane.showMessageDialog(
null,
"Please enter string with 5 letters.",
"Alert!",JOptionPane.ERROR_MESSAGE);
return false;
}
}
catch(ClassCastException exception)
{
return false;
}
return super.stopCellEditing();
}
public Component getTableCellEditorComponent(
JTable table, Object value, boolean isSelected, int row, int column)
{
Component c = super.getTableCellEditorComponent(
table, value, isSelected, row, column);
((JComponent)c).setBorder(new LineBorder(Color.black));
return c;
}
}
public static void main(String [] args)
{
JFrame frame = new TableEdit();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
}
I believe the correct way to do colouring in a table is via a ColorHighlighter. The table renderers have problems to render different colours in the same column.
Here is an example of how to use highlighters. In this case it is for highlighting a cell that is not editable.
public class IsCellEditablePredicate implements HighlightPredicate {
private JXTable table;
public IsCellEditablePredicate (final JXTable table) {
this.table = table;
}
#Override
public boolean isHighlighted(Component component, ComponentAdapter componentAdapter) {
return !table.isCellEditable(componentAdapter.row,
componentAdapter.column);
}
}
and then in your code for setuping the table you add the highlighter and its colour parameters:
ColorHighlighter grayHighlighter = new ColorHighlighter(new IsCellEditablePredicate(table));
grayHighlighter.setBackground(Color.LIGHT_GRAY);
grayHighlighter.setForeground(table.getForeground());
grayHighlighter.setSelectedBackground(table.getSelectionBackground().darker());
grayHighlighter.setSelectedForeground(table.getSelectionForeground().darker());
table.setHighlighters(grayHighlighter);
This is the simplest way to color a particular Column or cell in a jTable.
First just create a simple class of CustomRenderer
class CustomRenderer extends DefaultTableCellRenderer <br />
{
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);
setForeground(Color.blue); >
return c;
}
}
This code gets the column of cell to render
TableColumn col = tblExamHistoryAll.getColumnModel().getColumn(5);
DefaultTableModel model3 = (DefaultTableModel)tblExamHistoryAll.getModel();
col.setCellRenderer(new CustomRenderer());
This is to clear all previous rows from your table. If you do not want them just remove these lines
model3.getDataVector().removeAllElements();
model3.fireTableDataChanged();
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int col) {
Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col);
int control = row;
control = control % 2;
control = (control == 0) ? 1 : 0;
if (control == 1) {
c.setBackground(Color.green);
} else {
c.setBackground(Color.cyan);
}
return c;
}
The most straightforward way is to write a simple TableCellRenderer by extending the DefaultTableCellRenderer and overwriting the getTableCellRendererComponent method to setBackground( Color.RED ). For example:
final JTable table = new JTable(...);
table.setCellRenderer( new DefaultTableCellRenderer() {
public Component getTableCellRenderer(JTable table, Object value, ...) {
super.getTableCellRenderer(...);
if ( value should be highlighted ) {
setBackground( Color.RED );
}
return this;
}
});
You can extend DefaultTableCellRenderer, override getTableCellRendererComponent and call something like
if (myConditions) setBackground(myColor);
before returning "this" when conditions apply but it has a very annoying side-effect of changing the default back-color due to the way DefaultTableCellRenderer.setBackGround is coded.
The trick I found was to entirely duplicate the code of DefaultTableCellRenderer in a class named HackedDefaultTableCellRenderer, add a method that calls directly the Component's setBackground implementation:
public void setComponentBackground(Color c) {
super.setBackground(c);
}
then derive my customized rendered from this hacked class instead of from DefaultTableCellRenderer, and finally call setComponentBackground instead of setBackground in my customized getTableCellRendererComponent.
The drawback is that this HackedDefaultTableCellRenderer relies on a snapshot of DefaultTableCellRenderer.

Color Cell based on value

im using JXTable and trying to color the row based on value, BUT the show empty(but the render is running because it show the syso in console)
public class MyCellRenderer extends JLabel implements TableCellRenderer {
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int rowIndex, int vColIndex) {
SimpleTableModel mymodel = (SimpleTableModel) jxtableListar.getModel();
ModelProtocolo actualModel= (ModelProtocolo ) mymodel.getProtocolo(rowIndex) ;
if(actualModel.getValue() > 0) {
System.out.println("Yep the Render is working");
}
return this;
}
}
jxtableListar.setDefaultRenderer(Object.class, new MyCellRenderer ());
i did not found any usefull "tutorial" how to use the JXTable renderer, since they talk about something about Hightlight, but all the tutorials are "out of information" to learn
is there any good way/tutorial to color a JXTable row base on a value of the cell?
Your renderer is returning a JLabel (itself) that hasn't had anything set on it. Instead, extend DefaultTableCellRenderer:
public class MyCellRenderer extends DefaultTableCellRenderer {
#override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int rowIndex, int vColIndex) {
SimpleTableModel mymodel = (SimpleTableModel) jxtableListar.getModel();
ModelProtocolo actualModel= (ModelProtocolo ) mymodel.getProtocolo(rowIndex) ;
JLabel label = (JLabel) super.getTableCellRendererComponent(/* pass in all params */);
label.setText(/*whatever the text should be*/);
label.setBackground(/*whatever the color should be*/);
return label;
}
}

how to extend BooleanRenderer

I'm trying to implement mouse hover effects for my JTable.
(When the mouse goes over a table's row the row's background changes).
In order to do that, I extended the DefaultTableCellRenderer like this:
public class FileTableCellRenderer extends DefaultTableCellRenderer{
public FileTableCellRenderer() {
setOpaque(true);
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
FileTable fileTable = (FileTable)table;
Component c = super.getTableCellRendererComponent(fileTable, value, isSelected, hasFocus, row, column);
if(!isSelected){
if(row == fileTable.getCursorRow())
{
c.setBackground(Color.pink);
c.setForeground(Color.darkGray);
}
else
{
c.setBackground(Color.white);
c.setForeground(Color.darkGray);
}
}
this.setText(value.toString());
return this;
}
}
I set the JTable's defaultRenderer, and it works. The problem is I have one column which is Boolean. before I set my renderer I had this cute checkbox as default renderer for it.
Now, it just shows "true" or "false".
On the other hand, if I leave the defualt BooleanRenderer for the Boolean column, it will not be highlighted with the whole row...
I also tried to extned the JTable.BooleanRenderer, but it seems to be protected, so I cannot even extend it.
How can I leave this checkbox of the BooleanRenderer, but change background color with the rest of the row?
This cannot be done using inheritance since BooleanRenderer is a non-public inner class of JTable. But you can use composition instead. E.g., create a wrapper class that will accept a TableCellRenderer ('parent') as a constructor argument. If you pass a BooleanRenderer from your table as a parent, calling its getTableCellRendererComponent() method will return a Checkbox component, so you'll be able to make any further adjustments to it (I use code from the question to set the background color):
import javax.swing.*;
import javax.swing.plaf.UIResource;
import javax.swing.table.TableCellRenderer;
import java.awt.*;
public class BooleanCellRenderer implements TableCellRenderer, UIResource {
private final TableCellRenderer parent;
public BooleanCellRenderer(TableCellRenderer parent) {
this.parent = parent;
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
Component c = parent.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
FileTable fileTable = (FileTable) table;
if (!isSelected) {
if (row == fileTable.getCursorRow()) {
c.setBackground(Color.pink);
c.setForeground(Color.darkGray);
} else {
c.setBackground(Color.white);
c.setForeground(Color.darkGray);
}
}
return c;
}
}
Then, in your main GUI class containing a JTable, take the table's default Boolean renderer and pass it to the wrapper:
FileTable fileTable = new FileTable();
fileTable.setDefaultRenderer(Boolean.class, new BooleanCellRenderer(fileTable.getDefaultRenderer(Boolean.class)));
You can leave the FileTableCellRenderer to render String cells:
FileTable fileTable = new FileTable();
fileTable.setDefaultRenderer(String.class, new FileTableCellRenderer());
fileTable.setDefaultRenderer(Boolean.class, new

Scrollable Cells in JTable

I have a Jtable in which I have to show some big data. I cann't increase the size of the Cells So I need to add a scrollbar in each cell of the table through which I can scroll the text of cells.
I have tried to add a Custom Cell Renderer
private class ExtendedTableCellEditor extends AbstractCellEditor implements TableCellEditor
{
JLabel area = new JLabel();
String text;
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int rowIndex, int vColIndex)
{
area.setText(text);
return new JScrollPane(area);
}
public Object getCellEditorValue()
{
return text;
}
}
Now I am able to see the Scroll bar on the cells but not able to click and scroll them.
Any suggestions to this issue will be great.
Thanks in Advance.
Adding a JScrollPaneand placing the JLabel in the JScrollPane solved the issue. So I would like to share it with you all.
private class ExtendedTableCellEditor extends AbstractCellEditor implements TableCellEditor
{
JLabel _component = new JLabel();
JScrollPane _pane = new JScrollPane(_component, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
/**
* Returns the cell editor component.
*
*/
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int rowIndex, int vColIndex)
{
if (value == null) return null;
_component.setText(value != null ? value.toString() : "");
_component.setToolTipText(value != null ? value.toString() : "");
_component.setOpaque(true);
_component.setBackground((isSelected) ? Color.BLUE_DARK : Color.WHITE);
_component.setForeground((isSelected) ? Color.WHITE : Color.BLACK);
_pane.setHorizontalScrollBar(_pane.createHorizontalScrollBar());
_pane.setVerticalScrollBar(_pane.createVerticalScrollBar());
_pane.setBorder(new EmptyBorder(0,0,0,0));
_pane.setToolTipText(value != null ? value.toString() : "");
return _pane;
}
public Object getCellEditorValue()
{
return _component.getText();
}
}

How to get full highlighting (with border) on JTable Renderer

There is a common method when using JTable TableCellRenderers for setting the background and foreground when the cell is selected. Here is an example question that was asked:
Why does my Java custom cell renderer not show highlighting when the row/cell is selected?
This solution is lacking one thing ... the border around the cell. (Note I am not asking about a border around the row, as was asked here.) The border should highlight when the cell is selected. It is not acceptable to just create your own Border, and set it, because the border you create may not fit in with the Look & Feel.
I've successfully got the border by initializing a default renderer, and then scavenging it for its border, as follows:
private final DefaultTableCellRenderer defTblRend = new DefaultTableCellRenderer();
private final JComponent renderer = new ComplexCell(); // Whatever object type extends JComponent
#Override public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus, int row,
int column)
{
// ... Set values on "renderer" object here ...
renderer.setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
renderer.setForeground(isSelected ? table.getSelectionForeground() : table.getForeground());
renderer.setOpaque(!renderer.getBackground().equals(table.getBackground()));
JComponent comp = (JComponent)defTblRend.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
renderer.setBorder(comp.getBorder());
return renderer;
}
Is there a better way?
You might be able to use the UIManager. See UIManager Defaults. "Table.focusCellHighlightBorder" would appear to be the property you want.
ADDED BY ORIGINAL POSTER:
Here is the solution I came up with based on camickr's info. Optimizations/cleanup welcome.
Set up static borders so they are available wherever you need them (I put them in a class called "UiUtils"):
public static final Border focusedCellBorder = UIManager.getBorder("Table.focusCellHighlightBorder");
public static final Border unfocusedCellBorder = createEmptyBorder();
private static Border createEmptyBorder()
{
Insets i = focusedCellBorder.getBorderInsets(new JLabel());
return BorderFactory.createEmptyBorder(i.top, i.left, i.bottom, i.right);
}
Renderer
#Override public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column)
{
// [... set component values here ...]
label.setBorder(hasFocus ? UiUtils.focusedCellBorder : UiUtils.unfocusedCellBorder);
return label;
}

Categories

Resources