I am going to be using a lot of custom TableCellRenderers in a JTable, but I don't want to have to recreate the default properties for every single one, because it seems I have to start with a plain JLabel. This creates a ton of annoying overhead, because I even have to populate the value myself as well as the matching background, the foreground, the font, etc... to match the rest of the JTable.
Is there a way I can retrieve the parent TableCellRenderer or the it's resulting JLabel and manipulate that? That way I have all the defaults set already, and I can just manipulate the properties I am actually changing? I have tried everything with super.getCellRenderer and it is not giving me anything to accomplish that.
Also, my TableCellRenderer is not column-specific. Each cell can vary.
public class ActionTable extends JTable {
...
public TableCellRenderer getCellRenderer(int row, int column) {
String colName = ((ActionTableModel)this.getModel()).getColumnName(column);
if ( colName.equals("CUSTOM COL") ) {
return new ActionCellRenderer(this);
}
return super.getCellRenderer(this.convertRowIndexToModel(row), this.convertColumnIndexToModel(column));
}
public class ActionCellRenderer extends JLabel implements TableCellRenderer {
private ActionTable actionTable;
public ActionCellRenderer(ActionTable actionTable) {
this.actionTable = actionTable;
setOpaque(true); //MUST do this for background to show up.
}
#Override
public Component getTableCellRendererComponent(JTable table, Object color,boolean isSelected, boolean hasFocus,int row, int column) {
int modelRow = actionTable.convertRowIndexToModel(row);
int modelCol = actionTable.convertColumnIndexToModel(column);
String colName = table.getModel().getColumnName(modelCol);
/*
annoying overhead to retrieve default cell renderer properties
to match the rest of the JTable
*/
if (isSelected)
{
setBackground(table.getSelectionBackground());
setForeground(table.getSelectionForeground());
}
else
{
setBackground(table.getBackground());
setForeground(table.getForeground());
}
//AND I HAVE TO RETRIEVE THE VALUE MYSELF TOO
String textVal = ((ActionTableModel)table.getModel()).getValueAt(modelRow, modelCol).toString();
this.setHorizontalAlignment(SwingConstants.CENTER);
this.setFont(new Font("Serif", Font.PLAIN, 15));
this.setText(textVal);
//NOW I CAN DO CUSTOMIZATIONS
//PUT CUSTOMIZATIONS HERE
return this;
}
Just have your ActionCellRenderer extend DefaultTableCellRenderer and call the getTableCellRendererComponent() method to get the JLabel:
public class ActionCellRenderer extends DefaultTableCellRenderer{
public Component getTableCellRendererComponent(JTable table, Object value,boolean isSelected, boolean hasFocus,int row, int column){
//get the label
JLabel label = (JLabel)super.getTableCellRendererComponent(table, value,isSelected, hasFocus,row, column);
//do whatever you want with the label
return label;
}
}
Related
I am trying to change the color of the cells of the third row of my JTable if they have a value. I read that a good way to do this is using a table cell renderer. However, it just seems not to be doing anything! Here there is my RENDERER code:
public class RenderTablaPrestamos extends DefaultTableCellRenderer{
#Override
public Component getTableCellRendererComponent (JTable tabla, Object valor,
boolean isSelected, boolean hasFocus,
int row, int col){
JLabel celda = (JLabel) super.getTableCellRendererComponent(tabla, valor, isSelected, hasFocus, row, col);
if(valor instanceof Integer){
Integer v=(Integer)valor;
if(col==3){
if(valor!=null){
celda.setBackground(Color.red);
}
else{
celda.setBackground(Color.WHITE);
}
}
else{
celda.setBackground(Color.WHITE);
}
}
return celda;
}
}
Here there is how I use my renderer:
tablaUsuariosPrestamos.setDefaultRenderer(Object.class,new RenderTablaPrestamos());
Here there is a picture of my JTable (I do not think the model code would be any useful as it is kinda long):
I do not think it does anything to do with the if clausules, as I commented them and it did not work either.
Where am I going wrong?
Use
for (int i = 0; i < tabla.getColumnCount(); i++) {
tabla.getColumnModel().getColumn(i).setCellRenderer(new RenderTablaPrestamos());
}
instead of
tablaUsuariosPrestamos.setDefaultRenderer(Object.class,new RenderTablaPrestamos());
The code in setDefaultRenderer functions as intended:
final YourCellRenderer cellRenderer = new YourCellRenderer();
YourTableModel stModel = new YourTableModel();
table = new JTable(stModel);
table.setDefaultRenderer(YourComponent.class, cellRenderer);
The reason that it doesn't appear to render is because while you are mapping the YourComponent.class to the renderer, it's not firing because the YourTableModel thinks the class is an Object.
To correct this, you need to override the getColumnClass method within the YourTableModel class:
public class YourTableModel extends AbstractTableModel {
...
#Override
public Class<?> getColumnClass(int columnIndex) {
return YourComponent.class;
}
...
}
I have a JTable, that I want I want to be able to change the color of a single cell that is clicked on.
Here is a simplified version of my code:
public class TableFrame extends JFrame {
public TableFrame() {
JTable table = new JTable(8, 8);
table.setGridColor(Color.BLACK);
table.setDefaultRenderer(CustomCellRenderer.class, new CustomCellRenderer());
getContentPane().add(table);
}
public class CustomCellRenderer 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 (hasFocus) {
l.setBackground(Color.red);
l.setText("Hello");
}
return l;
}
}
}
When I click a certain cell, I expect it to change color to red and add "Hello" to it. It changes the text, but for some weird reason, it changes the color of all the cells after it? And when I click an uncolored cell, it does the same, but not always in an organised way if that makes sense? Like, it won't color all the cells after it, but maybe some that are just above and leave others blank..
It's really weird and makes no sense whatsoever. What is happening??
Having dug around the DefaultTableCellRenderer class a bit, when you call setBackground on the JLabel component, which is backing the DefaultTableCellRenderer, it is storing the value you use...
public void setBackground(Color c) {
super.setBackground(c);
unselectedBackground = c;
}
When the cell is painted again, it's this value (unselectedBackground) which is been used to repaint the cell in "default" mode...
if (isSelected) {
//...
} else {
Color background = unselectedBackground != null
? unselectedBackground
: table.getBackground();
if (background == null || background instanceof javax.swing.plaf.UIResource) {
Color alternateColor = DefaultLookup.getColor(this, ui, "Table.alternateRowColor");
if (alternateColor != null && row % 2 != 0) {
background = alternateColor;
}
}
super.setForeground(unselectedForeground != null
? unselectedForeground
: table.getForeground());
super.setBackground(background);
}
This means, the moment you use setBackground and pass it Color.RED, the DefaultTableCellRenderer assumes that this becomes the default color for ALL unselected cells.
The only choice you have is to reset the background color manually, for example...
public class CustomCellRenderer 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 (hasFocus) {
l.setBackground(Color.red);
l.setText("Hello");
} else if (!isSelected) {
l.setBackground(table.getBackground());
}
return l;
}
}
Also, you should really be using something more like...
table.setDefaultRenderer(Object.class, new CustomCellRenderer());
to register the cell renderer, as it's the Class type returned by TableModel#getColumnClass which determines which cell renderer is used ;)
Since the OP only wants help with the rendered and not with the data... here goes (assuming there is a function called hasBeenClicked(row,column) method available to determine whether the cell has been visited yet.
public class CustomCellRenderer 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 (hasBeenClicked(row,column)) {
l.setBackground(Color.red);
l.setText("Hello");
} else {
// reset the label to white background
l.setBackground(Color.white);
l.setText("Hello");
}
return l;
}
}
}
Also note that the registration of the renderer should be
table.setDefaultRenderer(Object.class, new CustomCellRenderer());
Since we want all columns to have this renderer (renderers are registered against the class of the column in the model).
I tested with the below as the hasBeenClicked method.
public boolean hasBeenClicked(int row, int column){
return (row%2==0 && column%2==0);
}
Just implement your own tracking of whether a cell has been clicked or not and you should be good to go. Remember that you should not use the renderer to track the clicks, use some kind of listener instead.
I've been looking around for a solution to this and I can't make head nor tail from various places of how to get my table to do coloured rows without asking my own question.
From every place I've looked I gather I need to use a cell renderer but the problem is I don't know how to apply it to my own situation.
So I have a simple JTable with 3 columns and I simply want each row to be highlighted in either green, yellow or red depending on the value of a separate variable (not displayed in the table).
It seems like it should be really simple but I can't get how to do it. If it helps my table is defined like:
studentTableModel = new DefaultTableModel(new Object[]{"Name", "StudentNo", "Part"}, 0);
jt_studentTable = new JTable(studentTableModel);
jt_studentTable.getColumnModel().getColumn(2).setPreferredWidth(10);
studentTableModel.addRow(new Object[]{"(empty)", "(empty)", "(empty)"});
JScrollPane jsp_tableScroller = new JScrollPane(jt_studentTable);
jsp_tableScroller.setPreferredSize(new Dimension(200,190));
middleCentrePanel.add(jsp_tableScroller);
The rows in the table change depending of the selection of a combo box.
Thanks in advance.
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.table.DefaultTableCellRenderer;
public class RowRendering {
private static Object[] columnName = {"Yes", "No"};
private static Object[][] data = {
{"Y", "N"},
{"N", "Y"},
{"Y", "N"}
};
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
JTable table = new JTable(data, columnName);
table.getColumnModel().getColumn(0).setCellRenderer(new CustomRenderer());
table.getColumnModel().getColumn(1).setCellRenderer(new CustomRenderer());
frame.add(new JScrollPane(table));
frame.setTitle("Rendering in JTable");
frame.pack();
frame.setVisible(true);
}
};
EventQueue.invokeLater(r);
}
}
class CustomRenderer extends DefaultTableCellRenderer
{
private static final long serialVersionUID = 6703872492730589499L;
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column)
{
Component cellComponent = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if(row == 0){
cellComponent.setBackground(Color.YELLOW);
} else if ( row == 1){
cellComponent.setBackground(Color.GRAY);
} else {
cellComponent.setBackground(Color.CYAN);
}
return cellComponent;
}
}
I simply want each row to be highlighted in either green, yellow or red depending on the value of a separate variable (not displayed in the table).
Renderers work on data in the table. That is components can only paint themselves when they have all the information needed to do the job.
So somehow you need to add the information to the table. This might be done be adding a 4th column that is hidden. Then the table still has access to the information required.
Then maybe you can use the suggestion in Table Row Renderering.
Maybe this works for you:
class MyCellRenderer extends DefaultTableCellRenderer {
String separatedVariable;
public MyCellRenderer(String separatedVariable) {
this.separatedVariable = separatedVariable;
}
#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);
c.setBackground(Color.WHITE);
c.setForeground(Color.BLACK);
JLabel l = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col);
if (separatedVariable.equals("YOUR VALUE TO GREEN")) {
l.setBackground(Color.GREEN);
return l;
} else {
if (separatedValue.equals("YOUR VALUE TO YELLOW")) {
l.setBackground(Color.YELLOW);
return l;
} else if (separatedValue.equals("YOUR VALUE TO RED")) {
l.setBaground(Color.RED);
return l;
}
}
return c;
}
}
I just had this same question, but a bit more complicated, since I already had several different renderers for each column, depending on the datatype.
But I found that this works like a charm:
public class MyTable extends JTable {
#Override
public Component prepareRenderer(TableCellRenderer renderer, int row, int column) {
Component result = super.prepareRenderer(renderer, row, column);
if (mustBeYellow(row, column)) {
result.setBackground(Color.yellow);
}
return result;
}
private boolean mustBeYellow(int row, int column) {
// implement this depending on your data..
return false;
}
}
I have a column in JTable which should display two types: String OR ImageIcon, not both. Each cell in that column has own thread which calculates data. In the beginning I put to each cell an image(like waiting logo), then REPLACE(not append) the image with a string of calculated data. I tried to extend default TableCell renderer, but it displays image like object address(javax.swing.ImageIcon#342...) and then replaces with string. Another variant, it displays the image correctly, but replaces it with empty string(or it is not visible?).
How to set it up so the table displays cell content correctly according to type?
Here is what I have at the moment:
class IconAndStringRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = 3606788739290618405L;
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, value,
isSelected, hasFocus, row, column);
if (value instanceof Icon) {
setIcon((Icon) value);
setText("");
}
return this;
}
}
Here is JTable:
table = new JTable(model) {
private static final long serialVersionUID = 8058795799817761161L;
public Class<?> getColumnClass(int column) {
if (column == TARGET_COLUMN)
return ImageIcon.class;
else
return super.getColumnClass(column);
}
};
A few more questions:
How to set it so the text replaces the image, not write text after image(even if it's not visible);
How to set text color, I gonna use setForebackground(Color c), but if I use it, the image is not dislayed.
Is it possible to make it working with Jlabel? Set up required Jlabel(with image or text) in the thread which modifies a cell and just setValueAt(label, row, column);
You need a custom renderer which can understand both types you use.
For example
public class IconAndStringRenderer extends DefaultTableCellRenderer {
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, value,
isSelected, hasFocus, row, column);
if (value instanceof Icon) {
setText("");
setIcon((Icon) value);
}
return this;
}
}
Try to set this class as your column renderer
The default renderer knows how to display both strings and icons. All you have to do is ensure that your TableModel returns the correct class from getColumnClass(), String.class and Icon.class, respectively. Examples may be found here.
Addendum: Here's a minimal example to illustrate the principle, based on default implementations.
Addendum: Not sure if it works when needed to return different classes for the same column.
If you really need to choose the renderer on a per-cell basis, override prepareRenderer(), as shown here.
import java.awt.EventQueue;
import javax.swing.Icon;
import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.table.DefaultTableModel;
/** #see https://stackoverflow.com/a/14672312/230513 */
public class Test {
private static final Icon YES = UIManager.getIcon("InternalFrame.maximizeIcon");
private static final Icon NO = UIManager.getIcon("InternalFrame.closeIcon");
private void display() {
JFrame f = new JFrame("Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
DefaultTableModel model = new DefaultTableModel(
new Object[]{"Name", "Icon"}, 0) {
#Override
public Class<?> getColumnClass(int col) {
if (col == 1) {
return Icon.class;
} else {
return super.getColumnClass(col);
}
}
};
model.addRow(new Object[]{"One", YES});
model.addRow(new Object[]{"Two", NO});
final JTable table = new JTable(model);
table.setRowHeight(YES.getIconHeight() +2);
f.add(table);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Test().display();
}
});
}
}
To take advantage of the default renderers you can override the getCellRenderer(...) method of JTable to return the appropriate renderer. Something like:
public TableCellRenderer getCellRenderer(int row, int column)
{
int modelColumn = convertColumnIndexToModel(column);
if (modelColumn == ???)
{
Class rowClass = getModel().getValueAt(row, modelColumn).getClass();
return getDefaultRenderer( rowClass );
}
else
return super.getCellRenderer(row, column);
}
I would like a simple way to put a JList in a column of a JTable. I already have the JLists and the table, but when put in the table, the Jlists are displayed as Strings, which is normal, because I use DefaultTableModel. I have overriden the getColumnClass() as:
public Class<? extends Object> getColumnClass(int c)
{
return getValueAt(0, c).getClass();
}
but this just formats the integer and float values.
I suppose that setValueAt() and getValueAt() should also be overriden, in order to return am array of Strings when I call JList.getSelectedValues(), but I can't figure out how.
I also want the cells to be editable, so the users can choose one or more option from the JList. After editing a row, I use a Save button to save the changes in a database, so I don't think I need a ListSelectionListener, JList.getSelectedValues() works just fine.
I know this is a common question, but I couldn't find an answer here. If this is a duplicate, please let me know and I will delete it.
I've done it. For everyone who needs the same thing, here is what I've done:
1)I have created a JScrollTableRenderer, and set the column I needed to show the JList to use this renderer
table.getColumnModel().getColumn(5).setCellRenderer(new JScrollTableRenderer());
The JScrollTableRenderer class content:
public class JScrollTableRenderer extends DefaultTableCellRenderer {
JScrollPane pane = new JScrollPane();
public JScrollTableRenderer()
{
super();
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column)
{
pane = (JScrollPane) value;
return pane;
}
}
2)I have created a JScrollTableEditor, and set the column I needed to show the JList to use this editor
table.getColumnModel().getColumn(5).setCellEditor(new JScrollTableEditor());
The JScrollTableEditor class content:
public class JScrollTableEditor extends AbstractCellEditor implements TableCellEditor {
JScrollPane component = new JScrollPane();
public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected,
int rowIndex, int vColIndex)
{
component = ((JScrollPane) value);
return ((JScrollPane) value);
}
public Object getCellEditorValue()
{
return component;
}
}
3)I added this method in the JTable model:
public Class<? extends Object> getColumnClass(int c)
{
if(c == 5) return JScrollPane.class;
else return getValueAt(0, c).getClass();
}