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;
}
Related
I use cellRenderer to change my cells (columns) backgroud color but it also make change the cell value format as LEFT columns on image, i'd keep the default format as the RIGHT column on image, here my simple code:
DefaultTableCellRenderer centerRenderer = new DefaultTableCellRenderer();
centerRenderer.setBackground(Color.ORANGE);
mytable.getColumnModel().getColumn(3).setCellRenderer( centerRenderer );
Thank you so much
here is the image as description above
You want to create a custom renderer that will allow you to format the data.
The basic logic would be something like:
public class IntegerRenderer extends DefaultTableCellRenderer
{
private Format formatter = new NumberFormage.getIntegerInstance();
public IntegerRenderer()
{
setHorizontalAlignment( SwingConstants.RIGHT );
}
public void setValue(Object value)
{
// Format the Object before setting its value in the renderer
try
{
if (value != null)
value = formatter.format(value);
}
catch(IllegalArgumentException e) {}
super.setValue(value);
}
}
Then you just set the background color of the renderer.
Check out Table Format Renderers for reusable classes that make is easy to create custom renders for numbers, percentages, dates etc.
It may be best to extend BasicTableCellRenderer to add those specific styles back in, as basic would have the bare minimum styles.
class CustomTableCellRenderer extends BasicTableCellRenderer {
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
// Add style logic here
return this;
}
}
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.
I want to add an actionListener to JTable cell value which means when the user clicks on cell text, an action going to call. The text of the cell value will show like a hyperlink. which allows the user to click on it.
Important note: I know how to add an action to the whole cell. My problem is that I want to add it to just a text like a link.
Try DefaultTableCellRenderer.getTableCellRendererComponent() to customize the look and feel of cell item.
Sample code:
public class CustomTableCellRenderer extends DefaultTableCellRenderer {
public Component getTableCellRendererComponent(JTable table, Object obj,
boolean isSelected, boolean hasFocus, int row, int column) {
JLabel label = (JLabel) super.getTableCellRendererComponent(table, obj, isSelected,
hasFocus, row, column);
label.setText(some_html_code_that_look_like_hyperlink);
}
}
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
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;
}