I've been following tutorials, learning this whole thing about JTable Renderers/Editors, but I got stuck trying to make a conditional renderer meant to display cells in different colors according to a set of conditions.
Here's the SSCCE I put together:
import java.awt.Color;
import java.awt.Component;
import java.awt.Font;
import javax.swing.AbstractCellEditor;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableColumnModel;
public class Demo2 extends javax.swing.JFrame {
public Demo2() {
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setSize(300, 300);
this.getContentPane().setLayout(new java.awt.GridLayout(1, 1));
Object[] colunas = {"Col 0"};
Object[][] dados = {{"A"},{"B"},{"C"},{"D"}};
JTable tbl = new JTable(dados,colunas);
TableColumnModel tcm = tbl.getColumnModel();
tcm.getColumn(0).setCellRenderer(new CustomCellRenderer());
JScrollPane sp = new JScrollPane(tbl);
this.add(sp);
}
class CustomCellRenderer extends DefaultTableCellRenderer {
#Override
public Component getTableCellRendererComponent(JTable jtable, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
Component c = super.getTableCellRendererComponent(jtable, value, isSelected, hasFocus, row, column);
if (value.toString().equals("B")) {
c.setForeground(Color.GREEN);
c.setFont(c.getFont().deriveFont(Font.BOLD));
} else if (value.toString().equals("D")) {
c.setForeground(Color.BLUE);
c.setFont(c.getFont().deriveFont(Font.BOLD));
}
return this;
}
}
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new Demo2().setVisible(true);
}
});
}
}
As I'd expect, the second cell has green foreground and is bold, and the 4th, blue and bold as well. However, the other cells seem to have been affected by the foreground color change as well.
Debugging with breakpoints, I see it executing setForeground in only the correct rows, though it executes both twice, which was unexpected.
Furthermore, if I change the last cell from "D" to "DD", then every cell's foreground turns green.
I think I'm missing something fundamental about renderers.
Where do you "un-bold" your font when it does not need to be bolded or change the colors back to the default? Remember that this isn't done automatically, but that your code needs to specifically do this. I think that you need to have more else blocks to be able to respond to all cases.
i.e.,
#Override
public Component getTableCellRendererComponent(JTable jtable, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
Component c = super.getTableCellRendererComponent(jtable, value, isSelected, hasFocus, row, column);
if (value.toString().equals("B")) {
c.setForeground(Color.GREEN);
c.setFont(c.getFont().deriveFont(Font.BOLD));
} else if (value.toString().equals("D")) {
c.setForeground(Color.BLUE);
c.setFont(c.getFont().deriveFont(Font.BOLD));
} else {
c.setForeground(null);
c.setFont(c.getFont().deriveFont(Font.PLAIN));
}
return this;
}
JTable cells are not like individual components but rather rendered images of a component. Consider them as being made with a cookie cutter. If you change the cutter for one cell and don't change it back, all subsequent cells will be effected.
Related
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've looked through a number of questions/answers on setting JTable header alignment, but they don't seem to cover this case. I want my JTable headers to have the same alignment as the data below them; it looks odd to me to have them all centered, especially for columns that have space for their occasional long strings but often contain only short strings -- the header is out in the center of the space by itself.
Many of the solutions I've seen depend on DefaultCellTableRenderer -- is that all the table headers use? I don't want to set the default renderer, since that would alter all columns; I want to write code to figure out the horizontal alignment for the data in the column, and set the header to do the same thing in terms of horizontal alignment.
Do I have to get the component from the renderer (using a specific row, column, and value) and set the alignment on the component? Do I have to write a custom renderer for the header just for this? What's the cleanest overall approach?
-- edit
Mr. Camick's solution is elegant and does almost what I want; it intercepts the creation of the table cell renderer for the column header. Somehow what I've done eliminates the normal formatting for a header, however -- there is no shading and no border on my headers (though they're aligned as I wanted!).
I'm currently have the following: where do I get the renderer for the table header as it is, so I can just change the alignment on it?
import java.awt.Component;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
public class TableHeaderRenderer implements TableCellRenderer
{
TableCellRenderer olderRenderer = null;
public TableHeaderRenderer(TableCellRenderer givenRenderer)
{
olderRenderer = givenRenderer;
}
// // get the default renderer for the table header.
// JTableHeader header = table.getTableHeader();
// TableCellRenderer defaultRenderer = header.getDefaultRenderer();
#Override
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column)
{
try // if the casts fail, we end up in the catch below
{
// get the renderer for the first row
DefaultTableCellRenderer firstRowTableCellRenderer = (DefaultTableCellRenderer)table.getCellRenderer(0, column);
// get the renderer for this column header
JTableHeader tableHeader = table.getTableHeader();
TableColumnModel columnModel = tableHeader.getColumnModel();
TableColumn tableColumn = columnModel.getColumn(column);
DefaultTableCellRenderer headerRenderer = (DefaultTableCellRenderer)tableColumn.getCellRenderer();
// if the renderer for the column is null, create one
if (headerRenderer == null)
{ headerRenderer = new DefaultTableCellRenderer();
}
// finally -- set the header's horizontal alignment to be the same as
// that for the first row, then get the component
headerRenderer.setHorizontalAlignment(firstRowTableCellRenderer.getHorizontalAlignment());
Component result = headerRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
return result;
}
catch (ClassCastException cce)
{
// either the first row or the header has a renderer that isn't a DefaultTableCellRenderer
// just return the component we already have
return olderRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
}
}
}
--
Ok, edit 2:
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader;
#SuppressWarnings("serial")
public class TableHeaderRendererDemo extends JFrame
{
Object[][]data = { {new Integer(1), "two", "three"},
{ new Integer(4), "five", "six" }
};
Object[] columnNames = { "first", "second", "third" };
public static void main(String ... arguments)
{ new TableHeaderRendererDemo().go();
}
public void go()
{
JTable table = new JTable(data, columnNames);
JScrollPane scrollPane = new JScrollPane(table);
// set our own default renderer for the headers
JTableHeader header = table.getTableHeader();
try
{ DefaultTableCellRenderer defaultRenderer = (DefaultTableCellRenderer)header.getDefaultRenderer();
header.setDefaultRenderer(new TableHeaderRenderer(defaultRenderer));
}
catch (ClassCastException e)
{
System.err.println("Could not cast default renderer; table headers not aligned");
}
add(scrollPane, BorderLayout.CENTER);
pack();
setVisible(true);
}
}
You can see the header problem I speak of; I hoped the Integers would default to right-justified, but for some reason they don't. But you can see the problem I'm having just from this.
Here is an example of a custom header renderer that simply makes the text of the selected column Bold:
class MyTableHeaderRenderer implements TableCellRenderer
{
private TableCellRenderer tableCellRenderer;
public MyTableHeaderRenderer(TableCellRenderer tableCellRenderer)
{
this.tableCellRenderer = tableCellRenderer;
}
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column)
{
Component c = tableCellRenderer.getTableCellRendererComponent(
table, value, isSelected, hasFocus, row, column);
if (column == table.getSelectedColumn())
c.setFont(getFont().deriveFont(Font.BOLD));
return c;
}
}
You would use the renderer with code like:
JTableHeader header = table.getTableHeader();
DefaultTableCellRenderer defaultRenderer = (DefaultTableCellRenderer)header.getDefaultRenderer();
header.setDefaultRenderer( new MyTableHeaderRenderer(defaultRenderer) );
In your case you would need to change the alignment, not the Font.
You might use code like the following:
TableCellRenderer tcr = table.getCellRenderer(0, column);
Component renderer = table.prepareRenderer(tcr, 0, column);
defaultRenderer.setAlignment( renderer.getAlignment() ); // whatever the alignment method is.
Edit:
Somehow what I've done eliminates the normal formatting for a header
You are using:
tableColumn.getCellRenderer();
should that not be:
tableColumn.getHeaderRenderer();
Rarely would people specify a special renderer for the table header by column. So if a header renderer is not specified for the TableColumn you should just use the default renderer of the JTableHeader, don't create a DefaultTableCellRenderer, this is NOT the renderer used by the table header.
Futzed around until I finally came on this; somehow the default renderer for Boolean is not a DefaultTableCellRenderer, so I can't get its alignment this way. I tried getting the component on the first row and getting ITS alignment, but that fails if there's no data in the table. This is good enough for what I want now. It seems to me that the alignment of the column has to live somewhere, and I still wish I could go get its value somehow, but so far all I've managed is a way to get it if the column data uses a DefaultTableCellRenderer.
Thanks to Messrs. camickr and MadProgrammer.
import java.awt.Component;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
/**
* Renderer that aligns the table's column header with the column's data;
* if the renderer for the first row of the column data cannot be cast to
* DefaultTableCellRenderer, this just sets the alignment to CENTER.
* <P>Use:
* <pre>
* // set our own default renderer for the headers
JTableHeader header = table.getTableHeader();
try
{ DefaultTableCellRenderer defaultRenderer = (DefaultTableCellRenderer)header.getDefaultRenderer();
header.setDefaultRenderer(new TableHeaderRenderer(defaultRenderer));
}
catch (ClassCastException e)
{
System.err.println("Could not cast default renderer; table headers not aligned");
}
*
*/
#SuppressWarnings("serial")
public class TableHeaderRenderer extends DefaultTableCellRenderer implements TableCellRenderer
{
DefaultTableCellRenderer innerRenderer = null;
public TableHeaderRenderer(TableCellRenderer givenRenderer)
{
// save the given renderer, normally the default for the header
innerRenderer = (DefaultTableCellRenderer)givenRenderer;
}
#Override
public Component getTableCellRendererComponent
(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
try
{
// get the renderer for the first row
DefaultTableCellRenderer firstRowTableCellRenderer = (DefaultTableCellRenderer)table.getCellRenderer(0, column);
int firstRowAlignment = firstRowTableCellRenderer.getHorizontalAlignment();
// set the alignment of this header to that of the first row
innerRenderer.setHorizontalAlignment(firstRowAlignment);
return innerRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, firstRowAlignment, column);
}
catch (ClassCastException cce)
{
// the first row has a renderer that isn't a DefaultTableCellRenderer
// just set the alignment to CENTER
innerRenderer.setHorizontalAlignment(SwingConstants.CENTER);
return innerRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
}
}
}
Before starting, I've viewed a handful of solutions as well as documentation. I can't seem to figure out why my code isn't working the way I believe it should work. I've extended DefaultTableCellRenderer but I don't believe it is being applied - that or I messed things up somewhere.
Here are the threads / websites I've looked into before posting this question:
Swing - Is it possible to set the font color of 'specific' text within a JTable cell?
JTable Cell Renderer
http://docs.oracle.com/javase/tutorial/uiswing/components/table.html
I realize the first link uses HTML to change the font color, but I would think the way I went about it should produce the same result.
To make it easier on those who want to help me figure out the issues, I've created an SSCCE.
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Font;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
public class TableTest {
private static final int IMPORTANT_COLUMN = 2;
public static void createAndShowGUI() {
Object[][] data = new Object[2][4];
//create sample data
String[] realRowData = { "1", "One", "1.0.2", "compile" };
String[] fakeRowData = { "2", "Two", "1.3.2-FAKE", "compile" };
//populate sample data
for(int i = 0; i < realRowData.length; i++) {
data[0][i] = realRowData[i];
data[1][i] = fakeRowData[i];
}
//set up tableModel
JTable table = new JTable();
table.setModel(new DefaultTableModel(data,
new String[] { "ID #", "Group #", "version", "Action" })
{
Class[] types = new Class[] {
Integer.class, String.class, String.class, String.class
};
boolean[] editable = new boolean[] {
false, false, true, false
};
#Override
public Class getColumnClass(int columnIndex) {
return types[columnIndex];
}
#Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return editable[columnIndex];
}
});
//set custom renderer on table
table.setDefaultRenderer(String.class, new CustomTableRenderer());
//create frame to place table
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setMinimumSize(new Dimension(400, 400));
JScrollPane scrollPane = new JScrollPane();
scrollPane.setViewportView(table);
f.add(scrollPane);
f.pack();
f.setVisible(true);
}
//MAIN
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
//Custom DefaultTableCellRenderer
public static class CustomTableRenderer extends DefaultTableCellRenderer {
public Component getTableCellRenderer(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column)
{
Component c = super.getTableCellRendererComponent(table, value, isSelected,
hasFocus, row, column);
String versionVal = table.getValueAt(row, IMPORTANT_COLUMN).toString();
if(versionVal.contains("FAKE")) {
//set to red bold font
c.setForeground(Color.RED);
c.setFont(new Font("Dialog", Font.BOLD, 12));
} else {
//stay at default
c.setForeground(Color.BLACK);
c.setFont(new Font("Dialog", Font.PLAIN, 12));
}
return c;
}
}
}
My goal is to highlight any value in the version column that contains the word FAKE in a red bold text.
I've extended DefaultTableCellRenderer but I don't believe it is being applied
Some simple debugging tips:
Add a simple System.out.println(...) to the method you think should be invoked
When overriding a method, make sure you use the #Override annotation (you used it in the TableModel class, but not your renderer class).
Your problem is a typing mistake because you are not overriding the proper method:
#Override
// public Component getTableCellRenderer(...) // this is wrong
public Component getTableCellRendererComponent(...)
The override annotation will display a compile message. Try it before changing the code.
Also, your first column is NOT an Integer class. Just because it contains String representations of an Integer does not make it an Integer. You need to add an Integer object to the model.
Replace your custom table cell rendere with the below.
Explanations are in comments. Basically, you should override getTableCellRendererComponent then check for correct column (there may be other methods instead of checking header value), then set cell depending on color.
Do not forget last else block to set color to default if it is not the column you want.
//Custom DefaultTableCellRenderer
public static class CustomTableRenderer extends DefaultTableCellRenderer {
// You should override getTableCellRendererComponent
#Override
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);
// Check the column name, if it is "version"
if (table.getColumnName(column).compareToIgnoreCase("version") == 0) {
// You know version column includes string
String versionVal = (String) value;
if (versionVal.contains("FAKE")) {
//set to red bold font
c.setForeground(Color.RED);
c.setFont(new Font("Dialog", Font.BOLD, 12));
} else {
//stay at default
c.setForeground(Color.BLACK);
c.setFont(new Font("Dialog", Font.PLAIN, 12));
}
} else {
// Here you should also stay at default
//stay at default
c.setForeground(Color.BLACK);
c.setFont(new Font("Dialog", Font.PLAIN, 12));
}
return c;
}
}
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 have a JTable with three columns in each row, see the image:
For some reason depending on the column i select i get the little dark blue border around it (V140116554) in the image above.
I currently use this to select the entire row:
vTable.setRowSelectionAllowed(true);
How can i disable this?
EDIT:
Added a class:
public class VisitorRenderer 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);
setBorder(noFocusBorder);
return this;
}
}
And added it:
vTable.setDefaultRenderer(String.class, new VisitorRenderer());
But still get the border
The TableCellRenderer is responsible for drawing the focus rectangle around the currently focused cell. You need to supply your own renderer that is capable of either overriding this feature or providing its own...
For example;
public class MyRenderer 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);
setBorder(noFocusBorder);
return this;
}
}
This uses the DefaultTableCellRenderer as the base renderer and sets the component's Border to noFocusBorder which is defined in DefaultTableCellRenderer as a EmptyBorder
You will then need to set this renderer as the default renderer for the effected columns. Check out How to use tables for more details
Update with example
Works fine for me...
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.LineBorder;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
public class TableRenderer {
public static void main(String[] args) {
new TableRenderer();
}
public TableRenderer() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
DefaultTableModel model = new DefaultTableModel(new Object[][]{{"", "One"}, {"", "Two"}}, new Object[]{"Check", "Vistor"}) {
#Override
public Class<?> getColumnClass(int columnIndex) {
return String.class;
}
};
JTable table = new JTable(model);
table.setRowSelectionAllowed(true);
table.setShowGrid(false);
table.setDefaultRenderer(String.class, new VisitorRenderer());
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(table));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class VisitorRenderer 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);
setBorder(noFocusBorder);
return this;
}
}
}
And just to be sure, I changed setBorder(noFocusBorder); to...
if (hasFocus) {
setBorder(new LineBorder(Color.RED));
}
From the looks of things, the visitor column class type isn't being reported as String by the TableModel...
Updated with proxy renderer concept
Because you want to remove the focus border from every cell. You have three choices...
Write a custom cell renderer for every possibility of Class type you might need for your table. This can time consuming and repeats a lot of code to achieve only a small effect.
Do nothing a live with it...
Use a "proxy" renderer. This is a renderer that uses another TableCellRenderer to perform the actual rendering process, but applies some minor changes to the result, for example, remove the border...
...
public static class ProxyCellRenderer implements TableCellRenderer {
protected static final Border DEFAULT_BORDER = new EmptyBorder(1, 1, 1, 1);
private TableCellRenderer renderer;
public ProxyCellRenderer(TableCellRenderer renderer) {
this.renderer = renderer;
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
Component comp = renderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (comp instanceof JComponent) {
((JComponent)comp).setBorder(DEFAULT_BORDER);
}
return comp;
}
}
Instead of doing something like...
table.setDefaultRenderer(String.class, new VisitorRenderer());
Which we did before, we would do this instead...
table.setDefaultRenderer(String.class,
new ProxyCellRenderer(table.getDefaultRenderer(String.class)));
This means we can take advantage of the what ever default renderer is already available without knowing what that might be, but also supply our own custom requirements to it...
Instead of creating your own TableCellRenderer you could also do:
#Override
public Component prepareRenderer(TableCellRenderer renderer, int row, int col) {
Component c = super.prepareRenderer(renderer, row, col);
if (c instanceof JComponent)
((JComponent)c).setBorder(new EmptyBorder(1, 1, 1, 1));
return c;
}
If you have absolutely no need for the border on any cell in that table, just apply MyRenderer to all cells, regardless of class. You can do it like this:
table.setDefaultRenderer(Object.class, new MyRenderer());